From 9155a9ea09950f6b6b38961f388c151c80a216e5 Mon Sep 17 00:00:00 2001 From: Quentin Tresontani Date: Wed, 25 Mar 2026 13:36:30 +0100 Subject: [PATCH 1/3] [WIP][Agent] Uptake new table for Agent Task Consumption --- .../Agent/Interaction/AgentTask.Codeunit.al | 12 +++ .../Internal/AgentTaskImpl.Codeunit.al | 15 +-- .../Internal/AgentTaskList.Page.al | 31 +++---- .../AgentConsumptionOverview.Page.al | 93 ++++++++++--------- .../AgentSystemPermissions.Codeunit.al | 4 +- .../App/Agent/Setup/AgentImpl.Codeunit.al | 7 -- .../App/Agent/Setup/AgentList.Page.al | 7 +- 7 files changed, 85 insertions(+), 84 deletions(-) diff --git a/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al b/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al index 99509811bf..f36c083556 100644 --- a/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al +++ b/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al @@ -148,6 +148,18 @@ codeunit 4303 "Agent Task" exit(AgentTaskImpl.GetCopilotCreditsConsumed(AgentTaskID)); end; + /// + /// Gets the details for the specified agent task log entry. + /// + /// The agent task log entry to get details for. + /// The details of the agent task log entry. + procedure GetDetailsForAgentTaskLogEntry(var AgentTaskLogEntry: Record "Agent Task Log Entry"): Text + var + AgentTaskImpl: Codeunit "Agent Task Impl."; + begin + exit(AgentTaskImpl.GetDetailsForAgentTaskLogEntry(AgentTaskLogEntry)); + end; + var FeatureAccessManagement: Codeunit "Feature Access Management"; diff --git a/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al b/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al index 47ff6de66d..a4e2efeaf2 100644 --- a/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al +++ b/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al @@ -7,7 +7,6 @@ namespace System.Agents; using System.Agents.Troubleshooting; using System.Environment; -using System.Environment.Consumption; using System.Integration; codeunit 4300 "Agent Task Impl." @@ -15,7 +14,6 @@ codeunit 4300 "Agent Task Impl." Access = Internal; InherentEntitlements = X; InherentPermissions = X; - Permissions = tabledata "User AI Consumption Data" = r; procedure SetMessageText(var AgentTaskMessage: Record "Agent Task Message"; MessageText: Text) var @@ -154,16 +152,19 @@ codeunit 4300 "Agent Task Impl." exit((AgentTask.Status = AgentTask.Status::"Stopped by User") or (AgentTask.Status = AgentTask.Status::"Stopped by System")); end; - procedure GetCopilotCreditsConsumed(AgentTaskID: BigInteger): Decimal + procedure GetCopilotCreditsConsumed(AgentTaskID: BigInteger) ConsumedCredits: Decimal var AgentTask: Record "Agent Task"; - UserAIConsumptionData: Record "User AI Consumption Data"; + AgentTaskConsumption: Record "Agent Task Consumption"; begin if not AgentTask.Get(AgentTaskID) then exit(0); - UserAIConsumptionData.SetRange("Agent Task Id", AgentTask.ID); - UserAIConsumptionData.CalcSums("Copilot Credits"); - exit(UserAIConsumptionData."Copilot Credits"); + + AgentTaskConsumption.SetRange("Task Id", AgentTask.ID); + if AgentTaskConsumption.FindSet() then + repeat + ConsumedCredits += AgentTaskConsumption."Copilot Credits"; + until AgentTaskConsumption.Next() = 0; end; internal procedure TryGetAgentRecordFromTaskId(TaskId: Integer; var Agent: Record Agent): Boolean diff --git a/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al b/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al index a1fdf1bca2..67bb75cc33 100644 --- a/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al +++ b/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al @@ -108,18 +108,18 @@ page 4300 "Agent Task List" } field(Credits; ConsumedCredits) { - Visible = ConsumedCreditsVisible; Caption = 'Copilot credits'; ToolTip = 'Specifies the number of Copilot credits consumed by the agent task.'; AutoFormatType = 0; DecimalPlaces = 0 : 2; + // Users can only see tasks they have access to, so we can expose the consumed credits. trigger OnDrillDown() var - UserAIConsumptionData: Record "User AI Consumption Data"; + AgentTaskConsumption: Record "Agent Task Consumption"; begin - UserAIConsumptionData.SetRange("Agent Task Id", Rec.ID); - Page.Run(Page::"Agent Consumption Overview", UserAIConsumptionData); + AgentTaskConsumption.SetRange("Task Id", Rec.ID); + Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); end; } } @@ -205,13 +205,6 @@ page 4300 "Agent Task List" } } - trigger OnOpenPage() - var - AgentSystemPermissions: Codeunit "Agent System Permissions"; - begin - ConsumedCreditsVisible := AgentSystemPermissions.CurrentUserCanSeeConsumptionData(); - end; - trigger OnAfterGetRecord() begin UpdateControls(); @@ -226,16 +219,15 @@ page 4300 "Agent Task List" local procedure CalculateTaskConsumedCredits() var - UserAIConsumptionData: Record "User AI Consumption Data"; + AgentTaskConsumption: Record "Agent Task Consumption"; begin - if not ConsumedCreditsVisible then begin - Clear(ConsumedCredits); - exit; - end; + ConsumedCredits := 0; - UserAIConsumptionData.SetRange("Agent Task Id", Rec.ID); - UserAIConsumptionData.CalcSums("Copilot Credits"); - ConsumedCredits := UserAIConsumptionData."Copilot Credits"; + AgentTaskConsumption.SetRange("Task Id", Rec.ID); + if AgentTaskConsumption.FindSet() then + repeat + ConsumedCredits += AgentTaskConsumption."Copilot Credits"; + until AgentTaskConsumption.Next() = 0; end; local procedure UpdateControls() @@ -258,5 +250,4 @@ page 4300 "Agent Task List" NumberOfStepsDone: Integer; TaskSelected: Boolean; ConsumedCredits: Decimal; - ConsumedCreditsVisible: Boolean; } \ No newline at end of file diff --git a/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al index e5e84b83f6..08e868012e 100644 --- a/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al +++ b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al @@ -6,8 +6,6 @@ namespace System.Agents; using System.Agents.Troubleshooting; -using System.Environment.Consumption; -using System.Security.AccessControl; /// /// This page is showing Copilot credit consumption per agent or per agent task @@ -16,12 +14,11 @@ page 4333 "Agent Consumption Overview" { PageType = Worksheet; ApplicationArea = All; - SourceTable = "User AI Consumption Data"; + SourceTable = "Agent Task Consumption"; Caption = 'Agent consumption overview'; InherentEntitlements = X; InherentPermissions = X; SourceTableView = sorting("Consumption DateTime") order(descending); - Permissions = tabledata User = r; InsertAllowed = false; ModifyAllowed = false; DeleteAllowed = false; @@ -30,6 +27,17 @@ page 4333 "Agent Consumption Overview" { area(Content) { + group(Info) + { + ShowCaption = false; + Visible = not CurrentUserIsAgentAdmin; + + label(NonAdminDisclaimer) + { + Caption = 'The consumption data shown only covers the agents you have access to and may be incomplete. Users with Agent-Admin have access to the complete consumption data.'; + } + } + group(Filters) { ShowCaption = false; @@ -83,7 +91,7 @@ page 4333 "Agent Consumption Overview" Caption = 'Resource name'; ToolTip = 'Specifies the name of the resource that consumed the credits. This is typically the type of the agent that performed the operation.'; } - field(UserName; UserName) + field(UserName; Rec."Agent User Display Name") { Caption = 'User name'; ToolTip = 'Specifies the name of the user who performed the operation.'; @@ -101,7 +109,7 @@ page 4333 "Agent Consumption Overview" { Caption = 'Copilot Studio feature'; } - field("Agent Task ID"; Rec."Agent Task ID") + field("Agent Task ID"; Rec."Task ID") { Caption = 'Agent task ID'; Enabled = not FilteredToTask; @@ -301,8 +309,7 @@ page 4333 "Agent Consumption Overview" var AgentSystemPermissions: Codeunit "Agent System Permissions"; begin - if not AgentSystemPermissions.CurrentUserCanSeeConsumptionData() then - Error(YourNotAuthorizedToViewMonetizationDataErr); + CurrentUserIsAgentAdmin := AgentSystemPermissions.CurrentUserHasCanManageAllAgentsPermission(); SetDateRangeFilters(); OnGetTotalsVisible(TotalsVisible, FilteredToTask); @@ -322,58 +329,55 @@ page 4333 "Agent Consumption Overview" local procedure UpdateRowValues() var - User: Record User; DescriptionInStream: InStream; begin - Rec.CalcFields(Description); + Rec.CalcFields(Description, "Agent User Display Name"); Rec.Description.CreateInStream(DescriptionInStream, TextEncoding::UTF8); DescriptionInStream.ReadText(DescriptionTxt); - if not UserNameDictionary.ContainsKey(Rec."User Id") then begin - if User.Get(Rec."User Id") then begin - UserName := User."User Name"; - UserNameDictionary.Add(Rec."User Id", UserName); - end else - UserName := ''; - - exit; - end; - - UserName := UserNameDictionary.Get(Rec."User Id"); end; local procedure UpdateAgentTaskName() var AgentTask: Record "Agent Task"; begin - if not AgentTaskDictionary.ContainsKey(Rec."Agent Task ID") then begin - if AgentTask.Get(Rec."Agent Task ID") then begin + // TODO(qutreson) task title could be a flow field. + if not AgentTaskDictionary.ContainsKey(Rec."Task ID") then begin + if AgentTask.Get(Rec."Task ID") then begin TaskNameTxt := StrSubstNo(AgentTaskNameTxt, AgentTask.ID, AgentTask.Title); - AgentTaskDictionary.Add(Rec."Agent Task ID", TaskNameTxt); + AgentTaskDictionary.Add(Rec."Task ID", TaskNameTxt); end else TaskNameTxt := ''; exit; end; - TaskNameTxt := AgentTaskDictionary.Get(Rec."Agent Task ID"); + TaskNameTxt := AgentTaskDictionary.Get(Rec."Task ID"); end; local procedure UpdateTotals() var - UserAIConsumptionData: Record "User AI Consumption Data"; + AgentTaskConsumption: Record "Agent Task Consumption"; begin if not TotalsVisible then exit; TotalEntriesCount := Rec.Count(); - UserAIConsumptionData.Copy(Rec); - UserAIConsumptionData.CalcSums("Copilot Credits"); - TotalEntriesCopilotCredits := UserAIConsumptionData."Copilot Credits"; - - UserAIConsumptionData.Copy(Rec); - UserAIConsumptionData.SetRange("Agent Task Id", Rec."Agent Task ID"); - UserAIConsumptionData.CalcSums("Copilot Credits"); - TotalTaskConsumedCredits := UserAIConsumptionData."Copilot Credits"; + AgentTaskConsumption.Copy(Rec); + TotalEntriesCopilotCredits := GetCopilotCreditsConsumed(AgentTaskConsumption); + + AgentTaskConsumption.Copy(Rec); + AgentTaskConsumption.SetRange("Task Id", Rec."Task ID"); + TotalTaskConsumedCredits := GetCopilotCreditsConsumed(AgentTaskConsumption); + end; + + local procedure GetCopilotCreditsConsumed(AgentTaskConsumption: Record "Agent Task Consumption") ConsumedCredits: Decimal + begin + if AgentTaskConsumption.FindSet() then + repeat + ConsumedCredits += AgentTaskConsumption."Copilot Credits"; + until AgentTaskConsumption.Next() = 0; + + exit(ConsumedCredits); end; local procedure UpdateTheDescriptionAndTotalsVisibility() @@ -382,7 +386,7 @@ page 4333 "Agent Consumption Overview" FilteredToTask := false; ShowFilters := true; - if (Rec.GetFilter("User Id") = '') and (Rec.GetFilter("Agent Task Id") = '') then begin + if (Rec.GetFilter("Agent User Security Id") = '') and (Rec.GetFilter("Task Id") = '') then begin ChangedDateRangeFilters := false; ConsumptionCaption := EverythingTok; TotalsVisible := false; @@ -390,7 +394,7 @@ page 4333 "Agent Consumption Overview" exit; end; - if Rec.GetFilter("Agent Task Id") <> '' then begin + if Rec.GetFilter("Task Id") <> '' then begin ConsumptionCaption := TaskNameTxt; FilteredToTask := true; ShowFilters := false; @@ -398,8 +402,8 @@ page 4333 "Agent Consumption Overview" exit; end; - if Rec.GetFilter("User Id") <> '' then begin - ConsumptionCaption := StrSubstNo(UserName); + if Rec.GetFilter("Agent User Security Id") <> '' then begin + ConsumptionCaption := Rec."Agent User Display Name"; FilteredToTask := false; exit; end; @@ -410,7 +414,7 @@ page 4333 "Agent Consumption Overview" if ChangedDateRangeFilters then exit; - if Rec.GetFilter("Agent Task Id") = '' then begin + if Rec.GetFilter("Task Id") = '' then begin EndDate := Today(); StartDate := CalcDate(StartDateTok, Today()); UpdateDateRange(StartDate, EndDate); @@ -437,16 +441,16 @@ page 4333 "Agent Consumption Overview" var AgentTaskLogEntry: Record "Agent Task Log Entry"; begin - AgentTaskLogEntry.SetRange("Task ID", Rec."Agent Task Id"); + AgentTaskLogEntry.SetRange("Task ID", Rec."Task ID"); Page.Run(Page::"Agent Task Log Entry List", AgentTaskLogEntry) end; local procedure DrillDownToAgentTaskConsumption() var - UserAIConsumptionData: Record "User AI Consumption Data"; + AgentTaskConsumption: Record "Agent Task Consumption"; begin - UserAIConsumptionData.SetRange("Agent Task Id", Rec."Agent Task ID"); - Page.Run(Page::"Agent Consumption Overview", UserAIConsumptionData); + AgentTaskConsumption.SetRange("Task Id", Rec."Task ID"); + Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); end; [IntegrationEvent(false, false)] @@ -456,12 +460,11 @@ page 4333 "Agent Consumption Overview" var AgentTaskDictionary: Dictionary of [BigInteger, Text]; - UserNameDictionary: Dictionary of [Guid, Text[80]]; + CurrentUserIsAgentAdmin: Boolean; ChangedDateRangeFilters: Boolean; StartDate: Date; EndDate: Date; DescriptionTxt: Text; - UserName: Text[80]; TotalEntriesCount: Integer; TaskNameTxt: Text; FilteredToTask: Boolean; diff --git a/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al b/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al index 6a763626b2..9cba1578cc 100644 --- a/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al +++ b/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al @@ -11,9 +11,9 @@ codeunit 4317 "Agent System Permissions" InherentPermissions = X; /// - /// Gets whether the current user has permissions to see consumption data. + /// Gets whether the current user has permissions to see all consumption data. /// - /// True if the user has permissions to see consumption data, false otherwise. + /// True if the user has permissions to see all consumption data, false otherwise. procedure CurrentUserCanSeeConsumptionData(): Boolean begin exit(AgentSystemPermissionsImpl.CurrentUserCanSeeConsumptionData()); diff --git a/src/System Application/App/Agent/Setup/AgentImpl.Codeunit.al b/src/System Application/App/Agent/Setup/AgentImpl.Codeunit.al index 5e5ffb6472..991b63d885 100644 --- a/src/System Application/App/Agent/Setup/AgentImpl.Codeunit.al +++ b/src/System Application/App/Agent/Setup/AgentImpl.Codeunit.al @@ -268,13 +268,6 @@ codeunit 4301 "Agent Impl." exit(false); end; - procedure CanShowMonetizationData(): Boolean - var - DummyUserAIConsumptionData: Record "User AI Consumption Data"; - begin - exit(DummyUserAIConsumptionData.ReadPermission()); - end; - local procedure UpdateUserSettingsWithProfile(var TempAllProfile: Record "All Profile" temporary; var UserSettingsRec: Record "User Settings") begin UserSettingsRec."Profile ID" := TempAllProfile."Profile ID"; diff --git a/src/System Application/App/Agent/Setup/AgentList.Page.al b/src/System Application/App/Agent/Setup/AgentList.Page.al index dc17a35fbc..7acd18236e 100644 --- a/src/System Application/App/Agent/Setup/AgentList.Page.al +++ b/src/System Application/App/Agent/Setup/AgentList.Page.al @@ -101,12 +101,13 @@ page 4316 "Agent List" trigger OnAction() var - UserAIConsumptionData: Record "User AI Consumption Data"; + AgentTaskConsumption: Record "Agent Task Consumption"; begin if Rec.IsEmpty() then Error(NoAgentSetupErr); - UserAIConsumptionData.SetRange("User ID", Rec."User Security ID"); - Page.Run(Page::"Agent Consumption Overview", UserAIConsumptionData); + + AgentTaskConsumption.SetRange("Agent User Security ID", Rec."User Security ID"); + Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); end; } } From f4592fdfffa1b4a5af1c47714e39a87374100010 Mon Sep 17 00:00:00 2001 From: Quentin Tresontani Date: Thu, 26 Mar 2026 15:16:52 +0100 Subject: [PATCH 2/3] Code review - address page opening scenarios and improve perf --- .../Internal/AgentTaskImpl.Codeunit.al | 6 +- .../Internal/AgentTaskList.Page.al | 1 - .../AgentConsumptionOverview.Page.al | 114 +++++++++++------- .../AgentSystemPermissions.Codeunit.al | 11 ++ .../AgentSystemPermissionsImpl.Codeunit.al | 13 ++ 5 files changed, 95 insertions(+), 50 deletions(-) diff --git a/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al b/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al index a4e2efeaf2..d7e33f02d6 100644 --- a/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al +++ b/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al @@ -161,10 +161,8 @@ codeunit 4300 "Agent Task Impl." exit(0); AgentTaskConsumption.SetRange("Task Id", AgentTask.ID); - if AgentTaskConsumption.FindSet() then - repeat - ConsumedCredits += AgentTaskConsumption."Copilot Credits"; - until AgentTaskConsumption.Next() = 0; + AgentTaskConsumption.CalcSums("Copilot Credits"); + exit(AgentTaskConsumption."Copilot Credits"); end; internal procedure TryGetAgentRecordFromTaskId(TaskId: Integer; var Agent: Record Agent): Boolean diff --git a/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al b/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al index 67bb75cc33..cffdef70a8 100644 --- a/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al +++ b/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al @@ -112,7 +112,6 @@ page 4300 "Agent Task List" ToolTip = 'Specifies the number of Copilot credits consumed by the agent task.'; AutoFormatType = 0; DecimalPlaces = 0 : 2; - // Users can only see tasks they have access to, so we can expose the consumed credits. trigger OnDrillDown() var diff --git a/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al index 08e868012e..19ae7c161f 100644 --- a/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al +++ b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al @@ -27,17 +27,6 @@ page 4333 "Agent Consumption Overview" { area(Content) { - group(Info) - { - ShowCaption = false; - Visible = not CurrentUserIsAgentAdmin; - - label(NonAdminDisclaimer) - { - Caption = 'The consumption data shown only covers the agents you have access to and may be incomplete. Users with Agent-Admin have access to the complete consumption data.'; - } - } - group(Filters) { ShowCaption = false; @@ -306,11 +295,8 @@ page 4333 "Agent Consumption Overview" } trigger OnOpenPage() - var - AgentSystemPermissions: Codeunit "Agent System Permissions"; begin - CurrentUserIsAgentAdmin := AgentSystemPermissions.CurrentUserHasCanManageAllAgentsPermission(); - + ValidateAgentAccessControl(); SetDateRangeFilters(); OnGetTotalsVisible(TotalsVisible, FilteredToTask); end; @@ -331,27 +317,14 @@ page 4333 "Agent Consumption Overview" var DescriptionInStream: InStream; begin - Rec.CalcFields(Description, "Agent User Display Name"); + Rec.CalcFields(Description, "Agent User Display Name", "Task Title"); Rec.Description.CreateInStream(DescriptionInStream, TextEncoding::UTF8); DescriptionInStream.ReadText(DescriptionTxt); end; local procedure UpdateAgentTaskName() - var - AgentTask: Record "Agent Task"; begin - // TODO(qutreson) task title could be a flow field. - if not AgentTaskDictionary.ContainsKey(Rec."Task ID") then begin - if AgentTask.Get(Rec."Task ID") then begin - TaskNameTxt := StrSubstNo(AgentTaskNameTxt, AgentTask.ID, AgentTask.Title); - AgentTaskDictionary.Add(Rec."Task ID", TaskNameTxt); - end else - TaskNameTxt := ''; - - exit; - end; - - TaskNameTxt := AgentTaskDictionary.Get(Rec."Task ID"); + TaskNameTxt := StrSubstNo(AgentTaskNameTxt, Rec."Task Id", Rec."Task Title"); end; local procedure UpdateTotals() @@ -363,21 +336,13 @@ page 4333 "Agent Consumption Overview" TotalEntriesCount := Rec.Count(); AgentTaskConsumption.Copy(Rec); - TotalEntriesCopilotCredits := GetCopilotCreditsConsumed(AgentTaskConsumption); + AgentTaskConsumption.CalcSums("Copilot Credits"); + TotalEntriesCopilotCredits := AgentTaskConsumption."Copilot Credits"; AgentTaskConsumption.Copy(Rec); AgentTaskConsumption.SetRange("Task Id", Rec."Task ID"); - TotalTaskConsumedCredits := GetCopilotCreditsConsumed(AgentTaskConsumption); - end; - - local procedure GetCopilotCreditsConsumed(AgentTaskConsumption: Record "Agent Task Consumption") ConsumedCredits: Decimal - begin - if AgentTaskConsumption.FindSet() then - repeat - ConsumedCredits += AgentTaskConsumption."Copilot Credits"; - until AgentTaskConsumption.Next() = 0; - - exit(ConsumedCredits); + AgentTaskConsumption.CalcSums("Copilot Credits"); + TotalTaskConsumedCredits := AgentTaskConsumption."Copilot Credits"; end; local procedure UpdateTheDescriptionAndTotalsVisibility() @@ -453,14 +418,72 @@ page 4333 "Agent Consumption Overview" Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); end; + local procedure ValidateAgentAccessControl() + var + AgentTask: Record "Agent Task"; + AgentSystemPermissions: Codeunit "Agent System Permissions"; + AgentUserSecurityId: Guid; + TaskId: BigInteger; + begin + RecallNonAdminDisclaimerNotification(); + + if AgentSystemPermissions.CurrentUserHasCanManageAllAgentsPermission() then + exit; + + // There is a filter on the agent user security, check that the user has access to the agent. + if Rec.GetFilter("Agent User Security Id") <> '' then begin + Evaluate(AgentUserSecurityId, Rec.GetFilter("Agent User Security Id")); + if not AgentSystemPermissions.CurrentUserCanUseAgent(AgentUserSecurityId) then + Error(YouDoNotHaveAccessToTheAgentErr); + exit; + end; + + // There is a filter on the task ID, check that the user has access to the agent. + if Rec.GetFilter("Task Id") <> '' then begin + Evaluate(TaskId, Rec.GetFilter("Task Id")); + if not AgentTask.Get(TaskId) then + Error(YouDoNotHaveAccessToTheAgentErr) + else + if not AgentSystemPermissions.CurrentUserCanUseAgent(AgentUserSecurityId) then + Error(YouDoNotHaveAccessToTheAgentErr); + exit; + end; + + SendNonAdminDisclaimerNotification(); + end; + + local procedure RecallNonAdminDisclaimerNotification() + var + NonAdminNotification: Notification; + begin + NonAdminNotification.Id := GetNonAdminDisclaimerNotificationId(); + NonAdminNotification.Recall(); + end; + + local procedure SendNonAdminDisclaimerNotification() + var + AgentSystemPermissions: Codeunit "Agent System Permissions"; + NonAdminNotification: Notification; + begin + if AgentSystemPermissions.CurrentUserHasCanManageAllAgentsPermission() then + exit; + + NonAdminNotification.Message := NonAdminDisclaimerMsg; + NonAdminNotification.Scope := NotificationScope::LocalScope; + NonAdminNotification.Send(); + end; + + local procedure GetNonAdminDisclaimerNotificationId(): Text + begin + exit('b9234f5f-3e6b-437a-9f34-acdb9bf9fb8f'); + end; + [IntegrationEvent(false, false)] local procedure OnGetTotalsVisible(var TotalsVisible: Boolean; var AgentTaskTotalsVisible: Boolean) begin end; var - AgentTaskDictionary: Dictionary of [BigInteger, Text]; - CurrentUserIsAgentAdmin: Boolean; ChangedDateRangeFilters: Boolean; StartDate: Date; EndDate: Date; @@ -476,7 +499,8 @@ page 4333 "Agent Consumption Overview" TotalsVisible: Boolean; EverythingTok: Label 'Everything'; AgentTaskNameTxt: Label 'Task #%1 - %2', Comment = '%1 - ID of the agent task, %2 - Title of the agent task'; - YourNotAuthorizedToViewMonetizationDataErr: Label 'You are missing the required permissions to view monetization data.'; TheEndDateIsTodayMsg: Label 'The end date is already set to today. You cannot move the date range filter further.'; + NonAdminDisclaimerMsg: Label 'You''re seeing consumption for agents you have access to. For complete data, ask an Agent-Admin.'; + YouDoNotHaveAccessToTheAgentErr: Label 'You do not have permission to view consumption data for this agent.'; StartDateTok: Label '<-CM>', Locked = true; } \ No newline at end of file diff --git a/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al b/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al index 9cba1578cc..f732463717 100644 --- a/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al +++ b/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al @@ -49,6 +49,17 @@ codeunit 4317 "Agent System Permissions" exit(AgentSystemPermissionsImpl.CurrentUserHasCanCreateCustomAgent()); end; + /// + /// Gets whether the current user has permissions to use a specific agent. + /// + /// The user security id associated with the agent. + /// True if the user has use permissions for the specified agent, false otherwise. + [Scope('OnPrem')] + procedure CurrentUserCanUseAgent(AgentUserSecurityId: Guid): Boolean + begin + exit(AgentSystemPermissionsImpl.CurrentUserCanUseAgent(AgentUserSecurityId)); + end; + var AgentSystemPermissionsImpl: Codeunit "Agent System Permissions Impl."; } \ No newline at end of file diff --git a/src/System Application/App/Agent/Permissions/Internal/AgentSystemPermissionsImpl.Codeunit.al b/src/System Application/App/Agent/Permissions/Internal/AgentSystemPermissionsImpl.Codeunit.al index fbe0fbd88d..91436fee0b 100644 --- a/src/System Application/App/Agent/Permissions/Internal/AgentSystemPermissionsImpl.Codeunit.al +++ b/src/System Application/App/Agent/Permissions/Internal/AgentSystemPermissionsImpl.Codeunit.al @@ -38,6 +38,19 @@ codeunit 4318 "Agent System Permissions Impl." exit(false); end; + procedure CurrentUserCanUseAgent(AgentUserSecurityId: Guid): Boolean + var + Agent: Record Agent; + begin + if (CurrentUserHasCanManageAllAgentsPermission()) then + exit(true); + + if Agent.Get(AgentUserSecurityId) then + exit(Agent."Can Current User Use Agent"); + + exit(false); + end; + local procedure CurrentUserHasExecuteSystemPermission(PermissionId: Integer): Boolean var TempPermission: Record "Expanded Permission" temporary; From 85dd693f24b77744f73da536242420269795993b Mon Sep 17 00:00:00 2001 From: Quentin Tresontani Date: Fri, 27 Mar 2026 17:45:45 +0100 Subject: [PATCH 3/3] Update code --- .../Agent/Interaction/AgentTask.Codeunit.al | 12 -- .../Internal/AgentTaskImpl.Codeunit.al | 13 -- .../Internal/AgentTaskList.Page.al | 15 +-- .../AgentConsumptionOverview.Codeunit.al | 67 +++++++++ .../AgentConsumptionOverview.Page.al | 15 +-- .../AgentSystemPermissions.Codeunit.al | 1 - .../App/Agent/Setup/AgentList.Page.al | 5 +- .../src/API/AITLogEntryAPI.Page.al | 10 +- .../src/Agent/AgentLogEntries.PageExt.al | 16 +-- .../src/Agent/AgentRunHistory.PageExt.al | 26 ++-- .../Agent/AgentTestConsumptionLog.Table.al | 50 +++++++ .../Agent/AgentTestContextImpl.Codeunit.al | 127 +++++++++++------- .../src/Agent/AgentTestMethodLines.PageExt.al | 16 +-- .../src/Agent/AgentTestSuite.PageExt.al | 18 +-- ...Log.Table.al => AgentTestTaskLog.Table.al} | 10 +- .../AITEvalMonthlyCopilotCred.Codeunit.al | 2 +- .../AITEvalMonthlyCopilotCred.Page.al | 45 ++++--- .../src/TestSuite/AITTestMethodLine.Table.al | 2 +- 18 files changed, 282 insertions(+), 168 deletions(-) create mode 100644 src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Codeunit.al create mode 100644 src/Tools/AI Test Toolkit/src/Agent/AgentTestConsumptionLog.Table.al rename src/Tools/AI Test Toolkit/src/Agent/{AgentTaskLog.Table.al => AgentTestTaskLog.Table.al} (93%) diff --git a/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al b/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al index f36c083556..782be7d4d6 100644 --- a/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al +++ b/src/System Application/App/Agent/Interaction/AgentTask.Codeunit.al @@ -136,18 +136,6 @@ codeunit 4303 "Agent Task" exit(AgentTaskImpl.IsTaskStopped(AgentTask)); end; - /// - /// Gets the total Copilot credits consumed by the agent task. - /// - /// The ID of the agent task to get consumed credits for. - /// The total Copilot credits consumed by the agent task. - procedure GetCopilotCreditsConsumed(AgentTaskID: BigInteger): Decimal - var - AgentTaskImpl: Codeunit "Agent Task Impl."; - begin - exit(AgentTaskImpl.GetCopilotCreditsConsumed(AgentTaskID)); - end; - /// /// Gets the details for the specified agent task log entry. /// diff --git a/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al b/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al index d7e33f02d6..ef89659c4c 100644 --- a/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al +++ b/src/System Application/App/Agent/Interaction/Internal/AgentTaskImpl.Codeunit.al @@ -152,19 +152,6 @@ codeunit 4300 "Agent Task Impl." exit((AgentTask.Status = AgentTask.Status::"Stopped by User") or (AgentTask.Status = AgentTask.Status::"Stopped by System")); end; - procedure GetCopilotCreditsConsumed(AgentTaskID: BigInteger) ConsumedCredits: Decimal - var - AgentTask: Record "Agent Task"; - AgentTaskConsumption: Record "Agent Task Consumption"; - begin - if not AgentTask.Get(AgentTaskID) then - exit(0); - - AgentTaskConsumption.SetRange("Task Id", AgentTask.ID); - AgentTaskConsumption.CalcSums("Copilot Credits"); - exit(AgentTaskConsumption."Copilot Credits"); - end; - internal procedure TryGetAgentRecordFromTaskId(TaskId: Integer; var Agent: Record Agent): Boolean var AgentTask: Record "Agent Task"; diff --git a/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al b/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al index cffdef70a8..ac60fabba2 100644 --- a/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al +++ b/src/System Application/App/Agent/Interaction/Internal/AgentTaskList.Page.al @@ -115,10 +115,9 @@ page 4300 "Agent Task List" trigger OnDrillDown() var - AgentTaskConsumption: Record "Agent Task Consumption"; + AgentConsumptionOverview: Codeunit "Agent Consumption Overview"; begin - AgentTaskConsumption.SetRange("Task Id", Rec.ID); - Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); + AgentConsumptionOverview.OpenAgentTaskConsumptionOverview(Rec.ID); end; } } @@ -218,15 +217,9 @@ page 4300 "Agent Task List" local procedure CalculateTaskConsumedCredits() var - AgentTaskConsumption: Record "Agent Task Consumption"; + AgentConsumptionOverview: Codeunit "Agent Consumption Overview"; begin - ConsumedCredits := 0; - - AgentTaskConsumption.SetRange("Task Id", Rec.ID); - if AgentTaskConsumption.FindSet() then - repeat - ConsumedCredits += AgentTaskConsumption."Copilot Credits"; - until AgentTaskConsumption.Next() = 0; + ConsumedCredits := AgentConsumptionOverview.GetCopilotCreditsConsumed(Rec.ID); end; local procedure UpdateControls() diff --git a/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Codeunit.al b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Codeunit.al new file mode 100644 index 0000000000..5d950456cb --- /dev/null +++ b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Codeunit.al @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.Agents; +using System.Environment.Consumption; + +codeunit 4333 "Agent Consumption Overview" +{ + InherentEntitlements = X; + InherentPermissions = X; + Permissions = tabledata "Agent Task" = r, + tabledata "User AI Consumption Data" = r; + + /// + /// Gets the total Copilot credits consumed by the agent task. + /// + /// The ID of the agent task to get consumed credits for. + /// The total Copilot credits consumed by the agent task. + procedure GetCopilotCreditsConsumed(TaskId: BigInteger): Decimal + var + UserAIConsumptionData: Record "User AI Consumption Data"; + begin + UserAIConsumptionData.SetRange("Agent Task Id", TaskId); + UserAIConsumptionData.CalcSums("Copilot Credits"); + exit(UserAIConsumptionData."Copilot Credits"); + end; + + /// + /// Opens the agent consumption overview page for the specified agent. + /// + /// The agent user security ID. + procedure OpenAgentConsumptionOverview(AgentUserSecurityId: Guid) + var + AgentTaskConsumption: Record "Agent Task Consumption"; + begin + AgentTaskConsumption.SetRange("Agent User Security ID", AgentUserSecurityId); + Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); + end; + + /// + /// Opens the agent consumption overview page for the specified agent task. + /// + /// The ID of the agent task. + procedure OpenAgentTaskConsumptionOverview(TaskId: BigInteger) + var + AgentTaskConsumption: Record "Agent Task Consumption"; + begin + AgentTaskConsumption.SetRange("Task ID", TaskId); + Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); + end; + + /// + /// Opens the agent consumption overview page for the agent tasks matching the specified filter. + /// + /// The filter for the agent task IDs. + procedure OpenAgentTaskConsumptionOverview(TaskIDFilter: Text) + var + AgentTaskConsumption: Record "Agent Task Consumption"; + AgentConsumptionOverview: Page "Agent Consumption Overview"; + begin + AgentTaskConsumption.SetFilter("Task ID", TaskIDFilter); + AgentConsumptionOverview.SetTableView(AgentTaskConsumption); + AgentConsumptionOverview.Run(); + end; +} \ No newline at end of file diff --git a/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al index 19ae7c161f..85f674a48c 100644 --- a/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al +++ b/src/System Application/App/Agent/Monetization/AgentConsumptionOverview.Page.al @@ -18,7 +18,7 @@ page 4333 "Agent Consumption Overview" Caption = 'Agent consumption overview'; InherentEntitlements = X; InherentPermissions = X; - SourceTableView = sorting("Consumption DateTime") order(descending); + SourceTableView = sorting("Consumption Timestamp") order(descending); InsertAllowed = false; ModifyAllowed = false; DeleteAllowed = false; @@ -59,7 +59,7 @@ page 4333 "Agent Consumption Overview" repeater(GroupName) { Editable = false; - field(ConsumptionDateTime; Rec."Consumption DateTime") + field(ConsumptionDateTime; Rec."Consumption Timestamp") { Caption = 'Created at'; ToolTip = 'Specifies the date and time when the consumption was created.'; @@ -389,7 +389,7 @@ page 4333 "Agent Consumption Overview" local procedure ClearDateRangeFilters() begin - Rec.SetRange("Consumption DateTime"); + Rec.SetRange("Consumption Timestamp"); Clear(StartDate); Clear(EndDate); end; @@ -399,7 +399,7 @@ page 4333 "Agent Consumption Overview" StartDate := NewStartDate; EndDate := NewEndDate; - Rec.SetRange("Consumption DateTime", CreateDateTime(StartDate, 0T), CreateDateTime(EndDate, 235959.999T)); + Rec.SetRange("Consumption Timestamp", CreateDateTime(StartDate, 0T), CreateDateTime(EndDate, 235959.999T)); end; local procedure DrillDownToAgentTask() @@ -412,10 +412,9 @@ page 4333 "Agent Consumption Overview" local procedure DrillDownToAgentTaskConsumption() var - AgentTaskConsumption: Record "Agent Task Consumption"; + AgentConsumptionOverview: Codeunit "Agent Consumption Overview"; begin - AgentTaskConsumption.SetRange("Task Id", Rec."Task ID"); - Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); + AgentConsumptionOverview.OpenAgentTaskConsumptionOverview(Rec."Task ID"); end; local procedure ValidateAgentAccessControl() @@ -444,7 +443,7 @@ page 4333 "Agent Consumption Overview" if not AgentTask.Get(TaskId) then Error(YouDoNotHaveAccessToTheAgentErr) else - if not AgentSystemPermissions.CurrentUserCanUseAgent(AgentUserSecurityId) then + if not AgentSystemPermissions.CurrentUserCanUseAgent(AgentTask."Agent User Security ID") then Error(YouDoNotHaveAccessToTheAgentErr); exit; end; diff --git a/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al b/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al index f732463717..2bd22c3ab4 100644 --- a/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al +++ b/src/System Application/App/Agent/Permissions/AgentSystemPermissions.Codeunit.al @@ -23,7 +23,6 @@ codeunit 4317 "Agent System Permissions" /// Gets whether the current user has permissions to manage all agents. /// /// True if the user has permissions to manage all agents, false otherwise. - [Scope('OnPrem')] procedure CurrentUserHasCanManageAllAgentsPermission(): Boolean begin exit(AgentSystemPermissionsImpl.CurrentUserHasCanManageAllAgentsPermission()); diff --git a/src/System Application/App/Agent/Setup/AgentList.Page.al b/src/System Application/App/Agent/Setup/AgentList.Page.al index 7acd18236e..a6edd4916d 100644 --- a/src/System Application/App/Agent/Setup/AgentList.Page.al +++ b/src/System Application/App/Agent/Setup/AgentList.Page.al @@ -101,13 +101,12 @@ page 4316 "Agent List" trigger OnAction() var - AgentTaskConsumption: Record "Agent Task Consumption"; + AgentConsumptionOverview: Codeunit "Agent Consumption Overview"; begin if Rec.IsEmpty() then Error(NoAgentSetupErr); - AgentTaskConsumption.SetRange("Agent User Security ID", Rec."User Security ID"); - Page.Run(Page::"Agent Consumption Overview", AgentTaskConsumption); + AgentConsumptionOverview.OpenAgentConsumptionOverview(Rec."User Security ID"); end; } } diff --git a/src/Tools/AI Test Toolkit/src/API/AITLogEntryAPI.Page.al b/src/Tools/AI Test Toolkit/src/API/AITLogEntryAPI.Page.al index 6a76958903..f6b3576753 100644 --- a/src/Tools/AI Test Toolkit/src/API/AITLogEntryAPI.Page.al +++ b/src/Tools/AI Test Toolkit/src/API/AITLogEntryAPI.Page.al @@ -139,13 +139,6 @@ page 149038 "AIT Log Entry API" } } - trigger OnOpenPage() - var - AgentSystemPermissions: Codeunit "Agent System Permissions"; - begin - ConsumedCreditsVisible := AgentSystemPermissions.CurrentUserCanSeeConsumptionData(); - end; - trigger OnAfterGetRecord() var AgentTestContextImpl: Codeunit "Agent Test Context Impl."; @@ -156,7 +149,7 @@ page 149038 "AIT Log Entry API" ErrorCallStackText := Rec.GetErrorCallStack(); SuiteDescription := Rec.GetSuiteDescription(); TestMethodLineDescription := Rec.GetTestMethodLineDescription(); - CopilotCredits := ConsumedCreditsVisible ? AgentTestContextImpl.GetCopilotCreditsForLogEntry(Rec."Entry No.") : -1; + CopilotCredits := AgentTestContextImpl.GetCopilotCreditsForLogEntry(Rec."Entry No."); AgentTaskIDs := AgentTestContextImpl.GetAgentTaskIDsForLogEntry(Rec."Entry No."); end; @@ -168,6 +161,5 @@ page 149038 "AIT Log Entry API" SuiteDescription: Text[250]; TestMethodLineDescription: Text[250]; CopilotCredits: Decimal; - ConsumedCreditsVisible: Boolean; AgentTaskIDs: Text; } \ No newline at end of file diff --git a/src/Tools/AI Test Toolkit/src/Agent/AgentLogEntries.PageExt.al b/src/Tools/AI Test Toolkit/src/Agent/AgentLogEntries.PageExt.al index 6461003efe..fc12a1edf4 100644 --- a/src/Tools/AI Test Toolkit/src/Agent/AgentLogEntries.PageExt.al +++ b/src/Tools/AI Test Toolkit/src/Agent/AgentLogEntries.PageExt.al @@ -20,7 +20,11 @@ pageextension 149030 "Agent Log Entries" extends "AIT Log Entries" Caption = 'Copilot Credits Consumed'; ToolTip = 'Specifies the total Copilot Credits consumed by the Agent Tasks for this log entry.'; Editable = false; - Visible = ConsumedCreditsVisible; + + trigger OnDrillDown() + begin + AgentTestContextImpl.OpenAgentConsumptionOverview(AgentTaskIDs); + end; } field("Agent Task IDs"; AgentTaskIDs) { @@ -39,13 +43,6 @@ pageextension 149030 "Agent Log Entries" extends "AIT Log Entries" } } - trigger OnOpenPage() - var - AgentSystemPermissions: Codeunit "Agent System Permissions"; - begin - ConsumedCreditsVisible := AgentSystemPermissions.CurrentUserCanSeeConsumptionData(); - end; - trigger OnAfterGetRecord() begin UpdateAgentTaskMetrics(); @@ -53,7 +50,7 @@ pageextension 149030 "Agent Log Entries" extends "AIT Log Entries" local procedure UpdateAgentTaskMetrics() begin - CopilotCredits := ConsumedCreditsVisible ? AgentTestContextImpl.GetCopilotCreditsForLogEntry(Rec."Entry No.") : -1; + CopilotCredits := AgentTestContextImpl.GetCopilotCreditsForLogEntry(Rec."Entry No."); AgentTaskIDs := AgentTestContextImpl.GetAgentTaskIDsForLogEntry(Rec."Entry No."); end; @@ -61,5 +58,4 @@ pageextension 149030 "Agent Log Entries" extends "AIT Log Entries" AgentTestContextImpl: Codeunit "Agent Test Context Impl."; CopilotCredits: Decimal; AgentTaskIDs: Text; - ConsumedCreditsVisible: Boolean; } diff --git a/src/Tools/AI Test Toolkit/src/Agent/AgentRunHistory.PageExt.al b/src/Tools/AI Test Toolkit/src/Agent/AgentRunHistory.PageExt.al index 62d5ddcb7e..c0e8818d55 100644 --- a/src/Tools/AI Test Toolkit/src/Agent/AgentRunHistory.PageExt.al +++ b/src/Tools/AI Test Toolkit/src/Agent/AgentRunHistory.PageExt.al @@ -17,10 +17,17 @@ pageextension 149032 "Agent Run History" extends "AIT Run History" { ApplicationArea = All; AutoFormatType = 0; - Visible = (ViewBy = ViewBy::Version) and ConsumedCreditsVisible; + Visible = ViewBy = ViewBy::Version; Caption = 'Copilot Credits Consumed'; ToolTip = 'Specifies the total Copilot Credits consumed by the Agent Tasks in the current version.'; Editable = false; + + trigger OnDrillDown() + var + AgentTestContextImpl: Codeunit "Agent Test Context Impl."; + begin + AgentTestContextImpl.OpenAgentConsumptionOverview(Rec."Agent Task IDs"); + end; } field("Agent Task Count - By Version"; AgentTaskCountByVersion) { @@ -44,10 +51,17 @@ pageextension 149032 "Agent Run History" extends "AIT Run History" { ApplicationArea = All; AutoFormatType = 0; - Visible = (ViewBy = ViewBy::Tag) and ConsumedCreditsVisible; + Visible = ViewBy = ViewBy::Tag; Caption = 'Copilot Credits Consumed'; ToolTip = 'Specifies the total Copilot Credits consumed by the Agent Tasks for the tag.'; Editable = false; + + trigger OnDrillDown() + var + AgentTestContextImpl: Codeunit "Agent Test Context Impl."; + begin + AgentTestContextImpl.OpenAgentTaskList(Rec."Agent Task IDs - By Tag"); + end; } field("Agent Task Count - By Tag"; AgentTaskCountByTag) { @@ -67,13 +81,6 @@ pageextension 149032 "Agent Run History" extends "AIT Run History" } } - trigger OnOpenPage() - var - AgentSystemPermissions: Codeunit "Agent System Permissions"; - begin - ConsumedCreditsVisible := AgentSystemPermissions.CurrentUserCanSeeConsumptionData(); - end; - trigger OnAfterGetRecord() begin UpdateAgentTaskCounts(); @@ -94,7 +101,6 @@ pageextension 149032 "Agent Run History" extends "AIT Run History" AgentTestContextImpl: Codeunit "Agent Test Context Impl."; AgentTaskCountByVersion: Integer; AgentTaskCountByTag: Integer; - ConsumedCreditsVisible: Boolean; } diff --git a/src/Tools/AI Test Toolkit/src/Agent/AgentTestConsumptionLog.Table.al b/src/Tools/AI Test Toolkit/src/Agent/AgentTestConsumptionLog.Table.al new file mode 100644 index 0000000000..5fe54bfb2d --- /dev/null +++ b/src/Tools/AI Test Toolkit/src/Agent/AgentTestConsumptionLog.Table.al @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.TestTools.AITestToolkit; + +table 149049 "Agent Test Consumption Log" +{ + Caption = 'Agent Eval Task Consumption Log'; + DataClassification = SystemMetadata; + Access = Internal; + ReplicateData = false; + DataPerCompany = false; + InherentEntitlements = RIMDX; + InherentPermissions = RIMDX; + + fields + { + field(1; "Entry No."; Integer) + { + Caption = 'Entry No.'; + AutoIncrement = true; + ToolTip = 'Specifies the Log Entry No..'; + } + field(2; "Agent Task ID"; BigInteger) + { + Caption = 'Agent Task ID'; + NotBlank = true; + ToolTip = 'Specifies the Agent Task ID.'; + } + field(3; "Copilot Credits"; Decimal) + { + Caption = 'Copilot Credits'; + ToolTip = 'Specifies the Copilot Credits consumed.'; + NotBlank = true; + } + } + + keys + { + key(Key1; "Entry No.") + { + Clustered = true; + } + key(Key2; "Agent Task ID") + { + } + } +} diff --git a/src/Tools/AI Test Toolkit/src/Agent/AgentTestContextImpl.Codeunit.al b/src/Tools/AI Test Toolkit/src/Agent/AgentTestContextImpl.Codeunit.al index 9ec4d53bac..820451f4cf 100644 --- a/src/Tools/AI Test Toolkit/src/Agent/AgentTestContextImpl.Codeunit.al +++ b/src/Tools/AI Test Toolkit/src/Agent/AgentTestContextImpl.Codeunit.al @@ -6,6 +6,7 @@ namespace System.TestTools.AITestToolkit; using System.Agents; +using System.Environment; using System.TestTools.TestRunner; codeunit 149049 "Agent Test Context Impl." @@ -46,20 +47,27 @@ codeunit 149049 "Agent Test Context Impl." local procedure LogAgentTask(AgentTaskId: BigInteger; var AITLogEntry: Record "AIT Log Entry") var - AgentTaskLog: Record "Agent Task Log"; + AgentTestTaskLog: Record "Agent Test Task Log"; + AgentTestConsumptionLog: Record "Agent Test Consumption Log"; + AgentConsumptionOverview: Codeunit "Agent Consumption Overview"; begin - AgentTaskLog.TransferFields(AITLogEntry, false); - AgentTaskLog."Agent Task ID" := AgentTaskId; - AgentTaskLog."Test Log Entry ID" := AITLogEntry."Entry No."; - AgentTaskLog.Insert(); + AgentTestTaskLog.TransferFields(AITLogEntry, false); + AgentTestTaskLog."Agent Task ID" := AgentTaskId; + AgentTestTaskLog."Test Log Entry ID" := AITLogEntry."Entry No."; + AgentTestTaskLog.Insert(); + + // Insert database level consumption tracking. + AgentTestConsumptionLog."Agent Task ID" := AgentTaskId; + AgentTestConsumptionLog."Copilot Credits" := AgentConsumptionOverview.GetCopilotCreditsConsumed(AgentTaskId); + AgentTestConsumptionLog.Insert(); end; procedure GetAgentTaskIDsForLogEntry(LogEntryNo: Integer): Text var - AgentTaskLog: Record "Agent Task Log"; + AgentTestTaskLog: Record "Agent Test Task Log"; begin - AgentTaskLog.SetRange("Test Log Entry ID", LogEntryNo); - exit(GetAgentTaskIDs(AgentTaskLog)); + AgentTestTaskLog.SetRange("Test Log Entry ID", LogEntryNo); + exit(GetCommaSeparatedAgentTaskIDs(AgentTestTaskLog)); end; procedure GetAgentTaskIDs(TestSuiteCode: Code[100]; VersionNumber: Integer; Tag: Text[20]; TestMethodLineNo: Integer): Text @@ -68,30 +76,31 @@ codeunit 149049 "Agent Test Context Impl." begin if VersionNumber > 0 then VersionFilterText := Format(VersionNumber); + exit(GetAgentTaskIDs(TestSuiteCode, VersionFilterText, Tag, TestMethodLineNo)); end; procedure GetAgentTaskIDs(TestSuiteCode: Code[100]; VersionFilter: Text; Tag: Text[20]; TestMethodLineNo: Integer): Text var - AgentTaskLog: Record "Agent Task Log"; + AgentTestTaskLog: Record "Agent Test Task Log"; begin - AgentTaskLog.SetRange("Test Suite Code", TestSuiteCode); + AgentTestTaskLog.SetRange("Test Suite Code", TestSuiteCode); if Tag <> '' then - AgentTaskLog.SetRange(Tag, Tag); + AgentTestTaskLog.SetRange(Tag, Tag); if VersionFilter <> '' then - AgentTaskLog.SetFilter(Version, VersionFilter); + AgentTestTaskLog.SetFilter(Version, VersionFilter); if TestMethodLineNo > 0 then - AgentTaskLog.SetRange("Test Method Line No.", TestMethodLineNo); + AgentTestTaskLog.SetRange("Test Method Line No.", TestMethodLineNo); - exit(GetAgentTaskIDs(AgentTaskLog)); + exit(GetCommaSeparatedAgentTaskIDs(AgentTestTaskLog)); end; procedure GetCopilotCreditsForLogEntry(LogEntryNo: Integer): Decimal var - AgentTaskLog: Record "Agent Task Log"; + AgentTestTaskLog: Record "Agent Test Task Log"; begin - AgentTaskLog.SetRange("Test Log Entry ID", LogEntryNo); - exit(GetCopilotCredits(AgentTaskLog)); + AgentTestTaskLog.SetRange("Test Log Entry ID", LogEntryNo); + exit(GetCopilotCredits(AgentTestTaskLog)); end; procedure GetCopilotCredits(TestSuiteCode: Code[100]; VersionNumber: Integer; Tag: Text[20]; TestMethodLineNo: Integer): Decimal @@ -105,34 +114,31 @@ codeunit 149049 "Agent Test Context Impl." procedure GetCopilotCredits(TestSuiteCode: Code[100]; VersionFilter: Text; Tag: Text[20]; TestMethodLineNo: Integer): Decimal var - AgentTaskLog: Record "Agent Task Log"; + AgentTestTaskLog: Record "Agent Test Task Log"; begin - AgentTaskLog.SetRange("Test Suite Code", TestSuiteCode); + AgentTestTaskLog.SetRange("Test Suite Code", TestSuiteCode); if VersionFilter <> '' then - AgentTaskLog.SetFilter(Version, VersionFilter); - + AgentTestTaskLog.SetFilter(Version, VersionFilter); if Tag <> '' then - AgentTaskLog.SetRange(Tag, Tag); - + AgentTestTaskLog.SetRange(Tag, Tag); if TestMethodLineNo > 0 then - AgentTaskLog.SetRange("Test Method Line No.", TestMethodLineNo); - - exit(GetCopilotCredits(AgentTaskLog)); + AgentTestTaskLog.SetRange("Test Method Line No.", TestMethodLineNo); + exit(GetCopilotCredits(AgentTestTaskLog)); end; - local procedure GetCopilotCredits(var AgentTaskLog: Record "Agent Task Log"): Decimal + local procedure GetCopilotCredits(var AgentTestTaskLog: Record "Agent Test Task Log"): Decimal var - AgentTask: Codeunit "Agent Task"; + AgentConsumptionOverview: Codeunit "Agent Consumption Overview"; TaskIDsList: List of [BigInteger]; TotalCredits: Decimal; begin - if AgentTaskLog.FindSet() then + if AgentTestTaskLog.FindSet() then repeat - if not TaskIDsList.Contains(AgentTaskLog."Agent Task ID") then begin - TaskIDsList.Add(AgentTaskLog."Agent Task ID"); - TotalCredits += AgentTask.GetCopilotCreditsConsumed(AgentTaskLog."Agent Task ID"); + if not TaskIDsList.Contains(AgentTestTaskLog."Agent Task ID") then begin + TaskIDsList.Add(AgentTestTaskLog."Agent Task ID"); + TotalCredits += AgentConsumptionOverview.GetCopilotCreditsConsumed(AgentTestTaskLog."Agent Task ID"); end; - until AgentTaskLog.Next() = 0; + until AgentTestTaskLog.Next() = 0; exit(TotalCredits); end; @@ -163,18 +169,30 @@ codeunit 149049 "Agent Test Context Impl." AgentTaskListPage.Run(); end; - local procedure GetAgentTaskIDs(var AgentTaskLog: Record "Agent Task Log"): Text + procedure OpenAgentConsumptionOverview(CommaSeparatedTaskIDs: Text) + var + AgentConsumptionOverview: Codeunit "Agent Consumption Overview"; + FilterText: Text; + begin + FilterText := ConvertCommaSeparatedToFilter(CommaSeparatedTaskIDs); + if FilterText = '' then + exit; + + AgentConsumptionOverview.OpenAgentTaskConsumptionOverview(FilterText); + end; + + local procedure GetCommaSeparatedAgentTaskIDs(var AgentTestTaskLog: Record "Agent Test Task Log"): Text var TaskIDList: List of [BigInteger]; TaskIDTextList: List of [Text]; begin - if AgentTaskLog.FindSet() then + if AgentTestTaskLog.FindSet() then repeat - if not TaskIDList.Contains(AgentTaskLog."Agent Task ID") then begin - TaskIDList.Add(AgentTaskLog."Agent Task ID"); - TaskIDTextList.Add(Format(AgentTaskLog."Agent Task ID")); + if not TaskIDList.Contains(AgentTestTaskLog."Agent Task ID") then begin + TaskIDList.Add(AgentTestTaskLog."Agent Task ID"); + TaskIDTextList.Add(Format(AgentTestTaskLog."Agent Task ID")); end; - until AgentTaskLog.Next() = 0; + until AgentTestTaskLog.Next() = 0; exit(ConcatenateList(TaskIDTextList, ', ')); end; @@ -227,24 +245,25 @@ codeunit 149049 "Agent Test Context Impl." procedure GetCopilotCreditsForPeriod(TestSuiteCode: Code[100]; PeriodStartDate: Date; PeriodEndDate: Date): Decimal var - AgentTaskLog: Record "Agent Task Log"; + AgentTestTaskLog: Record "Agent Test Task Log"; begin - AgentTaskLog.SetRange("Test Suite Code", TestSuiteCode); - AgentTaskLog.SetFilter(SystemCreatedAt, '>=%1&<=%2', CreateDateTime(PeriodStartDate, 0T), CreateDateTime(PeriodEndDate, 235959.999T)); - exit(GetCopilotCredits(AgentTaskLog)); + AgentTestTaskLog.SetRange("Test Suite Code", TestSuiteCode); + AgentTestTaskLog.SetFilter(SystemCreatedAt, '>=%1&<=%2', CreateDateTime(PeriodStartDate, 0T), CreateDateTime(PeriodEndDate, 235959.999T)); + exit(GetCopilotCredits(AgentTestTaskLog)); end; - procedure GetCopilotCreditsForPeriod(PeriodStartDate: Date): Decimal + procedure GetCopilotCreditsAcrossCompaniesForPeriod(PeriodStartDate: Date): Decimal begin - exit(GetCopilotCreditsForPeriod(PeriodStartDate, DT2Date(CurrentDateTime()))); + exit(GetCopilotCreditsAcrossCompaniesForPeriod(PeriodStartDate, DT2Date(CurrentDateTime()))); end; - procedure GetCopilotCreditsForPeriod(PeriodStartDate: Date; PeriodEndDate: Date): Decimal + procedure GetCopilotCreditsAcrossCompaniesForPeriod(PeriodStartDate: Date; PeriodEndDate: Date): Decimal var - AgentTaskLog: Record "Agent Task Log"; + AgentTestConsumptionLog: Record "Agent Test Consumption Log"; begin - AgentTaskLog.SetFilter(SystemCreatedAt, '>=%1&<=%2', CreateDateTime(PeriodStartDate, 0T), CreateDateTime(PeriodEndDate, 235959.999T)); - exit(GetCopilotCredits(AgentTaskLog)); + AgentTestConsumptionLog.SetFilter(SystemCreatedAt, '>=%1&<=%2', CreateDateTime(PeriodStartDate, 0T), CreateDateTime(PeriodEndDate, 235959.999T)); + AgentTestConsumptionLog.CalcSums("Copilot Credits"); + exit(AgentTestConsumptionLog."Copilot Credits"); end; [EventSubscriber(ObjectType::Codeunit, Codeunit::"Test Runner - Mgt", OnRunTestSuite, '', false, false)] @@ -264,6 +283,16 @@ codeunit 149049 "Agent Test Context Impl." Error(AgentIsNotActiveErr, AITTestSuite.Code, AITTestSuite."Agent User Security ID"); end; + [EventSubscriber(ObjectType::Codeunit, Codeunit::"System Action Triggers", GetAgentTaskEvalExecutionContext, '', true, true)] + local procedure GetAgentTaskEvalExecutionContextEvent(AgentUserSecurityId: Guid; TaskId: BigInteger; var Context: JsonObject) + var + AIMonthlyEvalCopilotCredits: Codeunit "AIT Eval Monthly Copilot Cred."; + LimitReachedTok: Label 'limitReached', Locked = true; + begin + // Agent task log entries are currently logged after the eval execution, so we need to answer independently of the task ID. + Context.Add(LimitReachedTok, AIMonthlyEvalCopilotCredits.IsLimitReached()); + end; + var AgentTaskList: List of [BigInteger]; GlobalAgentUserSecurityID: Guid; diff --git a/src/Tools/AI Test Toolkit/src/Agent/AgentTestMethodLines.PageExt.al b/src/Tools/AI Test Toolkit/src/Agent/AgentTestMethodLines.PageExt.al index 3189285855..905de123bf 100644 --- a/src/Tools/AI Test Toolkit/src/Agent/AgentTestMethodLines.PageExt.al +++ b/src/Tools/AI Test Toolkit/src/Agent/AgentTestMethodLines.PageExt.al @@ -20,7 +20,11 @@ pageextension 149033 "Agent Test Method Lines" extends "AIT Test Method Lines" Caption = 'Copilot Credits Consumed'; ToolTip = 'Specifies the total Copilot Credits consumed by the Agent Tasks for this eval line.'; Editable = false; - Visible = ConsumedCreditsVisible; + + trigger OnDrillDown() + begin + AgentTestContextImpl.OpenAgentConsumptionOverview(AgentTaskIDs); + end; } field("Agent Task Count"; AgentTaskCount) { @@ -39,13 +43,6 @@ pageextension 149033 "Agent Test Method Lines" extends "AIT Test Method Lines" } } - trigger OnOpenPage() - var - AgentSystemPermissions: Codeunit "Agent System Permissions"; - begin - ConsumedCreditsVisible := AgentSystemPermissions.CurrentUserCanSeeConsumptionData(); - end; - trigger OnAfterGetRecord() begin UpdateAgentTaskMetrics(); @@ -65,7 +62,7 @@ pageextension 149033 "Agent Test Method Lines" extends "AIT Test Method Lines" Rec.FilterGroup(4); VersionFilter := Rec.GetFilter(Rec."Version Filter"); Rec.FilterGroup(CurrentFilterGroup); - CopilotCredits := ConsumedCreditsVisible ? AgentTestContextImpl.GetCopilotCredits(Rec."Test Suite Code", VersionFilter, '', Rec."Line No.") : -1; + CopilotCredits := AgentTestContextImpl.GetCopilotCredits(Rec."Test Suite Code", VersionFilter, '', Rec."Line No."); AgentTaskIDs := AgentTestContextImpl.GetAgentTaskIDs(Rec."Test Suite Code", VersionFilter, '', Rec."Line No."); AgentTaskCount := AgentTestContextImpl.GetAgentTaskCount(AgentTaskIDs); end; @@ -75,5 +72,4 @@ pageextension 149033 "Agent Test Method Lines" extends "AIT Test Method Lines" CopilotCredits: Decimal; AgentTaskIDs: Text; AgentTaskCount: Integer; - ConsumedCreditsVisible: Boolean; } diff --git a/src/Tools/AI Test Toolkit/src/Agent/AgentTestSuite.PageExt.al b/src/Tools/AI Test Toolkit/src/Agent/AgentTestSuite.PageExt.al index 170192f164..05f1fc7d0e 100644 --- a/src/Tools/AI Test Toolkit/src/Agent/AgentTestSuite.PageExt.al +++ b/src/Tools/AI Test Toolkit/src/Agent/AgentTestSuite.PageExt.al @@ -58,7 +58,11 @@ pageextension 149034 "Agent Test Suite" extends "AIT Test Suite" Editable = false; Caption = 'Copilot Credits Consumed'; ToolTip = 'Specifies the total Copilot Credits consumed by the Agent Tasks in the current version.'; - Visible = ConsumedCreditsVisible; + + trigger OnDrillDown() + begin + AgentTestContextImpl.OpenAgentConsumptionOverview(AgentTaskIDs); + end; } field("Agent Task Count"; AgentTaskCount) { @@ -105,16 +109,9 @@ pageextension 149034 "Agent Test Suite" extends "AIT Test Suite" } } - trigger OnOpenPage() - var - AgentSystemPermissions: Codeunit "Agent System Permissions"; - begin - ConsumedCreditsVisible := AgentSystemPermissions.CurrentUserCanSeeConsumptionData(); - UpdateIsAgentTestType(); - end; - trigger OnAfterGetCurrRecord() begin + UpdateIsAgentTestType(); UpdateAgentTaskMetrics(); UpdateAgentUserName(); ShowNotifications(); @@ -127,7 +124,7 @@ pageextension 149034 "Agent Test Suite" extends "AIT Test Suite" local procedure UpdateAgentTaskMetrics() begin - CopilotCredits := ConsumedCreditsVisible ? AgentTestContextImpl.GetCopilotCredits(Rec.Code, Rec.Version, '', 0) : -1; + CopilotCredits := AgentTestContextImpl.GetCopilotCredits(Rec.Code, Rec.Version, '', 0); AgentTaskIDs := AgentTestContextImpl.GetAgentTaskIDs(Rec.Code, Rec.Version, '', 0); AgentTaskCount := AgentTestContextImpl.GetAgentTaskCount(AgentTaskIDs); end; @@ -189,7 +186,6 @@ pageextension 149034 "Agent Test Suite" extends "AIT Test Suite" AgentTaskIDs: Text; AgentTaskCount: Integer; AgentUserName: Code[50]; - ConsumedCreditsVisible: Boolean; IsAgentTestType: Boolean; AgentWithNameNotFoundErr: Label 'An agent with the name %1 was not found.', Comment = '%1 - The name of the agent'; } \ No newline at end of file diff --git a/src/Tools/AI Test Toolkit/src/Agent/AgentTaskLog.Table.al b/src/Tools/AI Test Toolkit/src/Agent/AgentTestTaskLog.Table.al similarity index 93% rename from src/Tools/AI Test Toolkit/src/Agent/AgentTaskLog.Table.al rename to src/Tools/AI Test Toolkit/src/Agent/AgentTestTaskLog.Table.al index ee36df594c..f5e495ba66 100644 --- a/src/Tools/AI Test Toolkit/src/Agent/AgentTaskLog.Table.al +++ b/src/Tools/AI Test Toolkit/src/Agent/AgentTestTaskLog.Table.al @@ -4,10 +4,11 @@ // ------------------------------------------------------------------------------------------------ namespace System.TestTools.AITestToolkit; +using System.Environment; -table 149050 "Agent Task Log" +table 149050 "Agent Test Task Log" { - Caption = 'AI Agent Task Log'; + Caption = 'Agent Eval Task Log'; DataClassification = SystemMetadata; Extensible = true; Access = Internal; @@ -27,7 +28,7 @@ table 149050 "Agent Task Log" { Caption = 'Eval Suite Code'; NotBlank = true; - TableRelation = "AIT Test Suite"; + TableRelation = "AIT Test Suite".Code; ToolTip = 'Specifies the Eval Suite Code.'; } field(3; "Test Method Line No."; Integer) @@ -89,5 +90,8 @@ table 149050 "Agent Task Log" key(Key3; "Test Log Entry ID", "Agent Task ID") { } + key(Key4; "Agent Task ID") + { + } } } diff --git a/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Codeunit.al b/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Codeunit.al index 3a9ac1e904..2fbbe0933d 100644 --- a/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Codeunit.al +++ b/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Codeunit.al @@ -51,7 +51,7 @@ codeunit 149039 "AIT Eval Monthly Copilot Cred." implements "AIT Eval Limit Prov if AITEvalMonthlyCopilotCreditsLimit."Monthly Credit Limit" <= 0 then exit(false); - CopilotCreditConsumed := AgentTestContextImpl.GetCopilotCreditsForPeriod(AITEvalMonthlyCopilotCreditsLimit.GetPeriodStartDate()); + CopilotCreditConsumed := AgentTestContextImpl.GetCopilotCreditsAcrossCompaniesForPeriod(AITEvalMonthlyCopilotCreditsLimit.GetPeriodStartDate()); exit(CopilotCreditConsumed >= AITEvalMonthlyCopilotCreditsLimit."Monthly Credit Limit"); end; diff --git a/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Page.al b/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Page.al index 82a9faa937..1778dfeff4 100644 --- a/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Page.al +++ b/src/Tools/AI Test Toolkit/src/Limits/CopilotCredits/AITEvalMonthlyCopilotCred.Page.al @@ -5,6 +5,8 @@ namespace System.TestTools.AITestToolkit; +using System.Agents; + page 149048 "AIT Eval Monthly Copilot Cred." { Caption = 'AI Eval Monthly Copilot Credit Limits'; @@ -31,10 +33,12 @@ page 149048 "AIT Eval Monthly Copilot Cred." group(CreditLimitSetup) { ShowCaption = false; + Enabled = CurrentUserIsAgentAdmin; field(EnforcementEnabled; EnforcementEnabled) { Caption = 'Limits Enabled'; ToolTip = 'Specifies whether the credit limit enforcement is enabled. When disabled, suites can consume unlimited credits.'; + Enabled = CurrentUserIsAgentAdmin; trigger OnValidate() begin @@ -46,9 +50,9 @@ page 149048 "AIT Eval Monthly Copilot Cred." { AutoFormatType = 0; Caption = 'Monthly Copilot Credit Limit'; - ToolTip = 'Specifies the maximum number of Copilot credits that can be consumed by all agent test suites during the current month.'; + ToolTip = 'Specifies the maximum number of Copilot credits that can be consumed by all agent test suites in this environment during the current month.'; DecimalPlaces = 2 : 5; - Editable = EnforcementEnabled; + Editable = EnforcementEnabled and CurrentUserIsAgentAdmin; trigger OnValidate() begin @@ -59,7 +63,7 @@ page 149048 "AIT Eval Monthly Copilot Cred." } repeater(AgentSuites) { - Caption = 'Agent Test Suites'; + Caption = 'Agent test suites in this company'; field("Code"; Rec."Suite Code") { @@ -81,7 +85,7 @@ page 149048 "AIT Eval Monthly Copilot Cred." { AutoFormatType = 0; Caption = 'Copilot Credits Consumed (Month)'; - ToolTip = 'Specifies the number of Copilot credits consumed by this test suite during the current month.'; + ToolTip = 'Specifies the number of Copilot credits consumed by this test suite in this environment during the current month.'; Editable = false; DecimalPlaces = 2 : 5; } @@ -99,7 +103,7 @@ page 149048 "AIT Eval Monthly Copilot Cred." { AutoFormatType = 0; Caption = 'Copilot Credits Consumed'; - ToolTip = 'Specifies the total number of Copilot credits consumed by all agent test suites during the current month, including credits from deleted suites.'; + ToolTip = 'Specifies the total number of Copilot credits consumed by all agent test suites in this environment during the current month, including credits from deleted suites.'; Editable = false; DecimalPlaces = 2 : 5; } @@ -165,6 +169,7 @@ page 149048 "AIT Eval Monthly Copilot Cred." DeletedSuiteCreditsConsumed: Decimal; LoadedDataCopilotCreditsConsumed: Decimal; EnforcementEnabled: Boolean; + CurrentUserIsAgentAdmin: Boolean; CreditsUsagePercentage: Text; CurrentPeriod: Text; CreditsAvailableStyle: Text; @@ -176,12 +181,20 @@ page 149048 "AIT Eval Monthly Copilot Cred." local procedure RefreshPage() begin + LoadCurrentUserIsAgentAdmin(); LoadCreditLimitSetup(); LoadBufferData(); UpdateComputedFields(); CurrPage.Update(false); end; + local procedure LoadCurrentUserIsAgentAdmin() + var + AgentSystemPermissions: Codeunit "Agent System Permissions"; + begin + CurrentUserIsAgentAdmin := AgentSystemPermissions.CurrentUserHasCanManageAllAgentsPermission(); + end; + local procedure LoadCreditLimitSetup() begin AITEvalMonthlyCopilotCreditLimitRecord.GetOrCreate(); @@ -201,7 +214,7 @@ page 149048 "AIT Eval Monthly Copilot Cred." local procedure LoadBufferData() var AITTestSuite: Record "AIT Test Suite"; - AIEvalSuiteUsageBufferTemp: Record "AIT Eval Suite Usage Buffer"; + TempAIEvalSuiteUsageBuffer: Record "AIT Eval Suite Usage Buffer"; AgentTestContextImpl: Codeunit "Agent Test Context Impl."; SortOrder: Integer; begin @@ -214,25 +227,25 @@ page 149048 "AIT Eval Monthly Copilot Cred." if AITTestSuite.FindSet() then repeat SortOrder += 1; - AIEvalSuiteUsageBufferTemp.Index := SortOrder; - AIEvalSuiteUsageBufferTemp."Suite Code" := AITTestSuite.Code; - AIEvalSuiteUsageBufferTemp."Suite Description" := AITTestSuite.Description; - AIEvalSuiteUsageBufferTemp.Consumed := AgentTestContextImpl.GetCopilotCreditsForPeriod(AITTestSuite.Code, AITEvalMonthlyCopilotCreditLimitRecord.GetPeriodStartDate()); - AIEvalSuiteUsageBufferTemp.Insert(); - LoadedDataCopilotCreditsConsumed += AIEvalSuiteUsageBufferTemp.Consumed; + TempAIEvalSuiteUsageBuffer.Index := SortOrder; + TempAIEvalSuiteUsageBuffer."Suite Code" := AITTestSuite.Code; + TempAIEvalSuiteUsageBuffer."Suite Description" := AITTestSuite.Description; + TempAIEvalSuiteUsageBuffer.Consumed := AgentTestContextImpl.GetCopilotCreditsForPeriod(AITTestSuite.Code, AITEvalMonthlyCopilotCreditLimitRecord.GetPeriodStartDate()); + TempAIEvalSuiteUsageBuffer.Insert(); + LoadedDataCopilotCreditsConsumed += TempAIEvalSuiteUsageBuffer.Consumed; until AITTestSuite.Next() = 0; // Sort the buffer by consumed credits in descending order. SortOrder := 0; - AIEvalSuiteUsageBufferTemp.SetCurrentKey(Consumed); + TempAIEvalSuiteUsageBuffer.SetCurrentKey(Consumed); #pragma warning disable AA0233, AA0181 - if AIEvalSuiteUsageBufferTemp.FindLast() then + if TempAIEvalSuiteUsageBuffer.FindLast() then repeat SortOrder += 1; - Rec := AIEvalSuiteUsageBufferTemp; + Rec := TempAIEvalSuiteUsageBuffer; Rec.Index := SortOrder; Rec.Insert(); - until AIEvalSuiteUsageBufferTemp.Next(-1) = 0; + until TempAIEvalSuiteUsageBuffer.Next(-1) = 0; #pragma warning restore AA0233, AA0181 if Rec.FindFirst() then; diff --git a/src/Tools/AI Test Toolkit/src/TestSuite/AITTestMethodLine.Table.al b/src/Tools/AI Test Toolkit/src/TestSuite/AITTestMethodLine.Table.al index b3494c474f..a390f7440c 100644 --- a/src/Tools/AI Test Toolkit/src/TestSuite/AITTestMethodLine.Table.al +++ b/src/Tools/AI Test Toolkit/src/TestSuite/AITTestMethodLine.Table.al @@ -206,7 +206,7 @@ table 149032 "AIT Test Method Line" ToolTip = 'Specifies the average accuracy of the eval line. The accuracy is calculated as the percentage of turns that passed or can be set manually by the eval.'; Editable = false; FieldClass = FlowField; - CalcFormula = average("AIT Log Entry"."Test Method Line Accuracy" where("Test Suite Code" = field("Test Suite Code"), "Test Method Line No." = field("Line No."), Version = field("Version Filter"), Operation = const('Run Procedure'), "Procedure Name" = filter(<> ''))); + CalcFormula = average("AIT Log Entry"."Test Method Line Accuracy" where("Test Suite Code" = field("Test Suite Code"), "Test Method Line No." = field("Line No."), Version = field("Version Filter"), Operation = const('Run Procedure'), "Procedure Name" = filter(<> ''), Status = filter(<> 2))); AutoFormatType = 0; } field(101; "AL Test Suite"; Code[10])