@memberjunction/ng-dashboards 5.38.0 → 5.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/README.md +14 -7
  2. package/dist/AI/components/agents/agent-configuration.component.js +199 -198
  3. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
  4. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts.map +1 -1
  5. package/dist/AI/components/analytics/ai-analytics-resource.component.js +20 -17
  6. package/dist/AI/components/analytics/ai-analytics-resource.component.js.map +1 -1
  7. package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts +15 -0
  8. package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts.map +1 -1
  9. package/dist/AI/components/analytics/cost-budget/cost-budget.component.js +166 -58
  10. package/dist/AI/components/analytics/cost-budget/cost-budget.component.js.map +1 -1
  11. package/dist/AI/components/analytics/executive-summary/executive-summary.component.d.ts.map +1 -1
  12. package/dist/AI/components/analytics/executive-summary/executive-summary.component.js +2 -1
  13. package/dist/AI/components/analytics/executive-summary/executive-summary.component.js.map +1 -1
  14. package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts +1 -0
  15. package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts.map +1 -1
  16. package/dist/AI/components/analytics/model-performance/model-performance.component.js +55 -36
  17. package/dist/AI/components/analytics/model-performance/model-performance.component.js.map +1 -1
  18. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts +9 -1
  19. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts.map +1 -1
  20. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js +158 -117
  21. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js.map +1 -1
  22. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts +1 -0
  23. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts.map +1 -1
  24. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js +22 -8
  25. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js.map +1 -1
  26. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +89 -856
  27. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
  28. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +1353 -7841
  29. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
  30. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.d.ts +87 -0
  31. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.d.ts.map +1 -0
  32. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.js +475 -0
  33. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.js.map +1 -0
  34. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.d.ts +29 -0
  35. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.d.ts.map +1 -0
  36. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.js +208 -0
  37. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.js.map +1 -0
  38. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.d.ts +21 -0
  39. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.d.ts.map +1 -0
  40. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.js +70 -0
  41. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.js.map +1 -0
  42. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts +235 -0
  43. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -0
  44. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +1735 -0
  45. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -0
  46. package/dist/AI/components/autotagging/shared/classify.dryrun.d.ts +61 -0
  47. package/dist/AI/components/autotagging/shared/classify.dryrun.d.ts.map +1 -0
  48. package/dist/AI/components/autotagging/shared/classify.dryrun.js +78 -0
  49. package/dist/AI/components/autotagging/shared/classify.dryrun.js.map +1 -0
  50. package/dist/AI/components/autotagging/shared/classify.format.d.ts +43 -0
  51. package/dist/AI/components/autotagging/shared/classify.format.d.ts.map +1 -0
  52. package/dist/AI/components/autotagging/shared/classify.format.js +209 -0
  53. package/dist/AI/components/autotagging/shared/classify.format.js.map +1 -0
  54. package/dist/AI/components/autotagging/shared/classify.types.d.ts +276 -0
  55. package/dist/AI/components/autotagging/shared/classify.types.d.ts.map +1 -0
  56. package/dist/AI/components/autotagging/shared/classify.types.js +6 -0
  57. package/dist/AI/components/autotagging/shared/classify.types.js.map +1 -0
  58. package/dist/AI/components/autotagging/tabs/health-tab.component.d.ts +103 -0
  59. package/dist/AI/components/autotagging/tabs/health-tab.component.d.ts.map +1 -0
  60. package/dist/AI/components/autotagging/tabs/health-tab.component.js +571 -0
  61. package/dist/AI/components/autotagging/tabs/health-tab.component.js.map +1 -0
  62. package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts +40 -0
  63. package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts.map +1 -0
  64. package/dist/AI/components/autotagging/tabs/history-tab.component.js +402 -0
  65. package/dist/AI/components/autotagging/tabs/history-tab.component.js.map +1 -0
  66. package/dist/AI/components/autotagging/tabs/inbox-tab.component.d.ts +107 -0
  67. package/dist/AI/components/autotagging/tabs/inbox-tab.component.d.ts.map +1 -0
  68. package/dist/AI/components/autotagging/tabs/inbox-tab.component.js +719 -0
  69. package/dist/AI/components/autotagging/tabs/inbox-tab.component.js.map +1 -0
  70. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts +122 -0
  71. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts.map +1 -0
  72. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js +752 -0
  73. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js.map +1 -0
  74. package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts +166 -0
  75. package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -0
  76. package/dist/AI/components/autotagging/tabs/sources-tab.component.js +1384 -0
  77. package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -0
  78. package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts +70 -0
  79. package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts.map +1 -0
  80. package/dist/AI/components/autotagging/tabs/tags-tab.component.js +448 -0
  81. package/dist/AI/components/autotagging/tabs/tags-tab.component.js.map +1 -0
  82. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.d.ts +397 -0
  83. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.d.ts.map +1 -0
  84. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.js +3490 -0
  85. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.js.map +1 -0
  86. package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts +47 -0
  87. package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts.map +1 -0
  88. package/dist/AI/components/autotagging/tabs/types-tab.component.js +220 -0
  89. package/dist/AI/components/autotagging/tabs/types-tab.component.js.map +1 -0
  90. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +293 -289
  91. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
  92. package/dist/AI/components/models/model-management.component.js +209 -208
  93. package/dist/AI/components/models/model-management.component.js.map +1 -1
  94. package/dist/AI/components/prompts/prompt-management.component.js +130 -128
  95. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  96. package/dist/AI/components/requests/agent-requests-resource.component.js +61 -61
  97. package/dist/AI/components/requests/agent-requests-resource.component.js.map +1 -1
  98. package/dist/AI/components/system/system-configuration.component.js +17 -17
  99. package/dist/AI/components/system/system-configuration.component.js.map +1 -1
  100. package/dist/AI/components/tags/tags-resource.component.js +550 -532
  101. package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
  102. package/dist/AI/components/vectors/vector-management-resource.component.js +1 -1
  103. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  104. package/dist/AI/services/ai-instrumentation.service.d.ts +5 -0
  105. package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -1
  106. package/dist/AI/services/ai-instrumentation.service.js +14 -2
  107. package/dist/AI/services/ai-instrumentation.service.js.map +1 -1
  108. package/dist/AI/services/cache-metrics.d.ts +50 -0
  109. package/dist/AI/services/cache-metrics.d.ts.map +1 -0
  110. package/dist/AI/services/cache-metrics.js +43 -0
  111. package/dist/AI/services/cache-metrics.js.map +1 -0
  112. package/dist/APIKeys/api-key-edit-panel.component.js +2 -2
  113. package/dist/APIKeys/api-keys-resource.component.js +132 -131
  114. package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
  115. package/dist/Actions/components/actions-overview.component.js +141 -141
  116. package/dist/Actions/components/actions-overview.component.js.map +1 -1
  117. package/dist/Actions/components/execution-monitoring.component.js +15 -15
  118. package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
  119. package/dist/Actions/components/explorer/action-explorer.component.d.ts +0 -5
  120. package/dist/Actions/components/explorer/action-explorer.component.d.ts.map +1 -1
  121. package/dist/Actions/components/explorer/action-explorer.component.js +139 -212
  122. package/dist/Actions/components/explorer/action-explorer.component.js.map +1 -1
  123. package/dist/Admin/admin-data-schema.component.js +2 -2
  124. package/dist/Admin/admin-data-schema.component.js.map +1 -1
  125. package/dist/Admin/admin-dev-tools-resource.component.js +2 -2
  126. package/dist/Admin/admin-dev-tools-resource.component.js.map +1 -1
  127. package/dist/Admin/admin-identity-access.component.js +2 -2
  128. package/dist/Admin/admin-identity-access.component.js.map +1 -1
  129. package/dist/Admin/admin-monitoring.component.js +2 -2
  130. package/dist/Admin/admin-monitoring.component.js.map +1 -1
  131. package/dist/ApplicationRoles/application-roles-resource.component.js +54 -49
  132. package/dist/ApplicationRoles/application-roles-resource.component.js.map +1 -1
  133. package/dist/Communication/communication-logs-resource.component.d.ts +6 -0
  134. package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
  135. package/dist/Communication/communication-logs-resource.component.js +72 -50
  136. package/dist/Communication/communication-logs-resource.component.js.map +1 -1
  137. package/dist/Communication/communication-monitor-resource.component.js +103 -102
  138. package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
  139. package/dist/Communication/communication-providers-resource.component.js +52 -51
  140. package/dist/Communication/communication-providers-resource.component.js.map +1 -1
  141. package/dist/Communication/communication-runs-resource.component.js +39 -38
  142. package/dist/Communication/communication-runs-resource.component.js.map +1 -1
  143. package/dist/Communication/communication-templates-resource.component.d.ts +6 -0
  144. package/dist/Communication/communication-templates-resource.component.d.ts.map +1 -1
  145. package/dist/Communication/communication-templates-resource.component.js +92 -89
  146. package/dist/Communication/communication-templates-resource.component.js.map +1 -1
  147. package/dist/Credentials/components/credentials-audit-resource.component.js +136 -135
  148. package/dist/Credentials/components/credentials-audit-resource.component.js.map +1 -1
  149. package/dist/Credentials/components/credentials-categories-resource.component.js +155 -152
  150. package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
  151. package/dist/Credentials/components/credentials-list-resource.component.js +119 -118
  152. package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
  153. package/dist/Credentials/components/credentials-overview-resource.component.js +129 -128
  154. package/dist/Credentials/components/credentials-overview-resource.component.js.map +1 -1
  155. package/dist/Credentials/components/credentials-types-resource.component.js +107 -106
  156. package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
  157. package/dist/DataExplorer/data-explorer-dashboard.component.js +2 -2
  158. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  159. package/dist/DatabaseDesigner/components/database-designer-dashboard.component.js +1 -1
  160. package/dist/DatabaseDesigner/components/entity-list.component.js +115 -114
  161. package/dist/DatabaseDesigner/components/entity-list.component.js.map +1 -1
  162. package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts +5 -6
  163. package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts.map +1 -1
  164. package/dist/DatabaseDesigner/database-designer-dashboards.module.js +4 -5
  165. package/dist/DatabaseDesigner/database-designer-dashboards.module.js.map +1 -1
  166. package/dist/DevTools/app-state-inspector.component.js +18 -17
  167. package/dist/DevTools/app-state-inspector.component.js.map +1 -1
  168. package/dist/DevTools/class-registry.component.js +88 -85
  169. package/dist/DevTools/class-registry.component.js.map +1 -1
  170. package/dist/DevTools/event-monitor.component.js +155 -150
  171. package/dist/DevTools/event-monitor.component.js.map +1 -1
  172. package/dist/DevTools/graphql-console.component.js +245 -243
  173. package/dist/DevTools/graphql-console.component.js.map +1 -1
  174. package/dist/DevTools/layout-inspector.component.js +18 -17
  175. package/dist/DevTools/layout-inspector.component.js.map +1 -1
  176. package/dist/EntityAdmin/entity-admin-dashboard.component.js +20 -19
  177. package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
  178. package/dist/Home/home-dashboard.component.js +2 -2
  179. package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
  180. package/dist/Integration/components/activity/activity.component.js +236 -229
  181. package/dist/Integration/components/activity/activity.component.js.map +1 -1
  182. package/dist/Integration/components/connections/connections.component.js +390 -389
  183. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  184. package/dist/Integration/components/overview/overview.component.js +2 -2
  185. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +2 -2
  186. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
  187. package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js +45 -44
  188. package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js.map +1 -1
  189. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +293 -291
  190. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
  191. package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js +62 -61
  192. package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js.map +1 -1
  193. package/dist/Lists/components/lists-browse-resource.component.d.ts +6 -2
  194. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  195. package/dist/Lists/components/lists-browse-resource.component.js +525 -566
  196. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  197. package/dist/Lists/components/lists-categories-resource.component.js +135 -134
  198. package/dist/Lists/components/lists-categories-resource.component.js.map +1 -1
  199. package/dist/Lists/components/lists-my-lists-resource.component.js +199 -198
  200. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  201. package/dist/MCP/mcp-dashboard.component.js +443 -438
  202. package/dist/MCP/mcp-dashboard.component.js.map +1 -1
  203. package/dist/Scheduling/components/scheduling-activity.component.d.ts.map +1 -1
  204. package/dist/Scheduling/components/scheduling-activity.component.js +146 -147
  205. package/dist/Scheduling/components/scheduling-activity.component.js.map +1 -1
  206. package/dist/Scheduling/components/scheduling-jobs.component.js +76 -75
  207. package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
  208. package/dist/Scheduling/components/scheduling-overview.component.js +97 -96
  209. package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
  210. package/dist/Scheduling/scheduling-dashboard.component.js +24 -22
  211. package/dist/Scheduling/scheduling-dashboard.component.js.map +1 -1
  212. package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts +2 -0
  213. package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts.map +1 -1
  214. package/dist/Scheduling/services/scheduling-instrumentation.service.js +1 -0
  215. package/dist/Scheduling/services/scheduling-instrumentation.service.js.map +1 -1
  216. package/dist/Testing/components/testing-dashboard-tab-resource.component.js +1 -1
  217. package/dist/Testing/components/testing-explorer.component.d.ts +14 -4
  218. package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -1
  219. package/dist/Testing/components/testing-explorer.component.js +436 -427
  220. package/dist/Testing/components/testing-explorer.component.js.map +1 -1
  221. package/dist/Testing/components/testing-runs-resource.component.js +1 -1
  222. package/dist/Testing/components/testing-runs.component.js +116 -115
  223. package/dist/Testing/components/testing-runs.component.js.map +1 -1
  224. package/dist/Testing/testing-dashboard.component.js +6 -7
  225. package/dist/Testing/testing-dashboard.component.js.map +1 -1
  226. package/dist/VersionHistory/components/labels-resource.component.js +173 -172
  227. package/dist/VersionHistory/components/labels-resource.component.js.map +1 -1
  228. package/dist/VersionHistory/components/restore-resource.component.d.ts +6 -0
  229. package/dist/VersionHistory/components/restore-resource.component.d.ts.map +1 -1
  230. package/dist/VersionHistory/components/restore-resource.component.js +116 -92
  231. package/dist/VersionHistory/components/restore-resource.component.js.map +1 -1
  232. package/dist/ai-dashboards.module.d.ts +47 -35
  233. package/dist/ai-dashboards.module.d.ts.map +1 -1
  234. package/dist/ai-dashboards.module.js +40 -1
  235. package/dist/ai-dashboards.module.js.map +1 -1
  236. package/dist/communication-dashboards.module.d.ts +1 -1
  237. package/dist/communication-dashboards.module.d.ts.map +1 -1
  238. package/dist/communication-dashboards.module.js +7 -1
  239. package/dist/communication-dashboards.module.js.map +1 -1
  240. package/dist/testing-dashboards.module.d.ts +4 -5
  241. package/dist/testing-dashboards.module.d.ts.map +1 -1
  242. package/dist/testing-dashboards.module.js +7 -5
  243. package/dist/testing-dashboards.module.js.map +1 -1
  244. package/package.json +55 -54
@@ -7,6 +7,8 @@
7
7
  import { Component, Input, Output, EventEmitter, ChangeDetectorRef, inject } from '@angular/core';
8
8
  import { Subject } from 'rxjs';
9
9
  import { RunView } from '@memberjunction/core';
10
+ import { NormalizeUUID } from '@memberjunction/global';
11
+ import { cacheHitRate, hasCacheActivity, netCacheSavings } from '../../../services/cache-metrics';
10
12
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
11
13
  import * as i0 from "@angular/core";
12
14
  import * as i1 from "@memberjunction/ng-shared-generic";
@@ -20,14 +22,14 @@ function AnalyticsCostBudgetComponent_Conditional_0_Template(rf, ctx) { if (rf &
20
22
  i0.ɵɵelementEnd();
21
23
  } }
22
24
  function AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Conditional_1_Template(rf, ctx) { if (rf & 1) {
23
- i0.ɵɵelement(0, "i", 30);
25
+ i0.ɵɵelement(0, "i", 32);
24
26
  } }
25
27
  function AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Conditional_2_Template(rf, ctx) { if (rf & 1) {
26
- i0.ɵɵelement(0, "i", 31);
28
+ i0.ɵɵelement(0, "i", 33);
27
29
  } }
28
30
  function AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Template(rf, ctx) { if (rf & 1) {
29
- i0.ɵɵelementStart(0, "div", 29);
30
- i0.ɵɵconditionalCreate(1, AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Conditional_1_Template, 1, 0, "i", 30)(2, AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Conditional_2_Template, 1, 0, "i", 31);
31
+ i0.ɵɵelementStart(0, "div", 31);
32
+ i0.ɵɵconditionalCreate(1, AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Conditional_1_Template, 1, 0, "i", 32)(2, AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Conditional_2_Template, 1, 0, "i", 33);
31
33
  i0.ɵɵtext(3);
32
34
  i0.ɵɵpipe(4, "number");
33
35
  i0.ɵɵelementEnd();
@@ -40,16 +42,16 @@ function AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Template
40
42
  i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(4, 6, kpi_r2.Delta, "1.1-1"), "% vs prev ");
41
43
  } }
42
44
  function AnalyticsCostBudgetComponent_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
43
- i0.ɵɵelementStart(0, "div", 23)(1, "div", 24);
45
+ i0.ɵɵelementStart(0, "div", 25)(1, "div", 26);
44
46
  i0.ɵɵelement(2, "i");
45
47
  i0.ɵɵelementEnd();
46
- i0.ɵɵelementStart(3, "div", 25)(4, "div", 26);
48
+ i0.ɵɵelementStart(3, "div", 27)(4, "div", 28);
47
49
  i0.ɵɵtext(5);
48
50
  i0.ɵɵelementEnd();
49
- i0.ɵɵelementStart(6, "div", 27);
51
+ i0.ɵɵelementStart(6, "div", 29);
50
52
  i0.ɵɵtext(7);
51
53
  i0.ɵɵelementEnd();
52
- i0.ɵɵconditionalCreate(8, AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Template, 5, 9, "div", 28);
54
+ i0.ɵɵconditionalCreate(8, AnalyticsCostBudgetComponent_Conditional_1_For_2_Conditional_8_Template, 5, 9, "div", 30);
53
55
  i0.ɵɵelementEnd()();
54
56
  } if (rf & 2) {
55
57
  const kpi_r2 = ctx.$implicit;
@@ -69,7 +71,7 @@ function AnalyticsCostBudgetComponent_Conditional_1_Conditional_10_Template(rf,
69
71
  i0.ɵɵelementEnd();
70
72
  } }
71
73
  function AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_Conditional_2_Template(rf, ctx) { if (rf & 1) {
72
- i0.ɵɵelementStart(0, "div", 35)(1, "span", 36);
74
+ i0.ɵɵelementStart(0, "div", 37)(1, "span", 38);
73
75
  i0.ɵɵtext(2, "avg");
74
76
  i0.ɵɵelementEnd()();
75
77
  } if (rf & 2) {
@@ -77,9 +79,9 @@ function AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_Conditional_2
77
79
  i0.ɵɵstyleProp("bottom", ctx_r2.AvgLinePercent, "%");
78
80
  } }
79
81
  function AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_For_4_Template(rf, ctx) { if (rf & 1) {
80
- i0.ɵɵelementStart(0, "div", 34);
81
- i0.ɵɵelement(1, "div", 37);
82
- i0.ɵɵelementStart(2, "div", 38);
82
+ i0.ɵɵelementStart(0, "div", 36);
83
+ i0.ɵɵelement(1, "div", 39);
84
+ i0.ɵɵelementStart(2, "div", 40);
83
85
  i0.ɵɵtext(3);
84
86
  i0.ɵɵelementEnd()();
85
87
  } if (rf & 2) {
@@ -93,9 +95,9 @@ function AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_For_4_Templat
93
95
  i0.ɵɵtextInterpolate(bar_r4.Label);
94
96
  } }
95
97
  function AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_Template(rf, ctx) { if (rf & 1) {
96
- i0.ɵɵelementStart(0, "div", 11)(1, "div", 32);
97
- i0.ɵɵconditionalCreate(2, AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_Conditional_2_Template, 3, 2, "div", 33);
98
- i0.ɵɵrepeaterCreate(3, AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_For_4_Template, 4, 6, "div", 34, _forTrack2);
98
+ i0.ɵɵelementStart(0, "div", 11)(1, "div", 34);
99
+ i0.ɵɵconditionalCreate(2, AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_Conditional_2_Template, 3, 2, "div", 35);
100
+ i0.ɵɵrepeaterCreate(3, AnalyticsCostBudgetComponent_Conditional_1_Conditional_11_For_4_Template, 4, 6, "div", 36, _forTrack2);
99
101
  i0.ɵɵelementEnd()();
100
102
  } if (rf & 2) {
101
103
  const ctx_r2 = i0.ɵɵnextContext(2);
@@ -110,15 +112,15 @@ function AnalyticsCostBudgetComponent_Conditional_1_Conditional_18_Template(rf,
110
112
  i0.ɵɵelementEnd();
111
113
  } }
112
114
  function AnalyticsCostBudgetComponent_Conditional_1_Conditional_19_For_2_Template(rf, ctx) { if (rf & 1) {
113
- i0.ɵɵelementStart(0, "div", 40);
115
+ i0.ɵɵelementStart(0, "div", 42);
114
116
  i0.ɵɵpipe(1, "number");
115
- i0.ɵɵelementStart(2, "span", 41);
117
+ i0.ɵɵelementStart(2, "span", 43);
116
118
  i0.ɵɵtext(3);
117
119
  i0.ɵɵelementEnd();
118
- i0.ɵɵelementStart(4, "span", 42);
120
+ i0.ɵɵelementStart(4, "span", 44);
119
121
  i0.ɵɵtext(5);
120
122
  i0.ɵɵelementEnd();
121
- i0.ɵɵelementStart(6, "span", 43);
123
+ i0.ɵɵelementStart(6, "span", 45);
122
124
  i0.ɵɵtext(7);
123
125
  i0.ɵɵpipe(8, "number");
124
126
  i0.ɵɵelementEnd()();
@@ -136,49 +138,56 @@ function AnalyticsCostBudgetComponent_Conditional_1_Conditional_19_For_2_Templat
136
138
  } }
137
139
  function AnalyticsCostBudgetComponent_Conditional_1_Conditional_19_Template(rf, ctx) { if (rf & 1) {
138
140
  i0.ɵɵelementStart(0, "div", 15);
139
- i0.ɵɵrepeaterCreate(1, AnalyticsCostBudgetComponent_Conditional_1_Conditional_19_For_2_Template, 9, 14, "div", 39, _forTrack0);
141
+ i0.ɵɵrepeaterCreate(1, AnalyticsCostBudgetComponent_Conditional_1_Conditional_19_For_2_Template, 9, 14, "div", 41, _forTrack0);
140
142
  i0.ɵɵelementEnd();
141
143
  } if (rf & 2) {
142
144
  const ctx_r2 = i0.ɵɵnextContext(2);
143
145
  i0.ɵɵadvance();
144
146
  i0.ɵɵrepeater(ctx_r2.TreemapCells);
145
147
  } }
146
- function AnalyticsCostBudgetComponent_Conditional_1_Conditional_51_Template(rf, ctx) { if (rf & 1) {
147
- i0.ɵɵelementStart(0, "tr")(1, "td", 44);
148
+ function AnalyticsCostBudgetComponent_Conditional_1_Conditional_55_Template(rf, ctx) { if (rf & 1) {
149
+ i0.ɵɵelementStart(0, "tr")(1, "td", 46);
148
150
  i0.ɵɵtext(2, "No data available");
149
151
  i0.ɵɵelementEnd()();
150
152
  } }
151
- function AnalyticsCostBudgetComponent_Conditional_1_For_53_Template(rf, ctx) { if (rf & 1) {
152
- i0.ɵɵelementStart(0, "tr")(1, "td", 45);
153
+ function AnalyticsCostBudgetComponent_Conditional_1_For_57_Template(rf, ctx) { if (rf & 1) {
154
+ i0.ɵɵelementStart(0, "tr")(1, "td", 47);
153
155
  i0.ɵɵtext(2);
154
156
  i0.ɵɵelementEnd();
155
- i0.ɵɵelementStart(3, "td", 46);
157
+ i0.ɵɵelementStart(3, "td", 48);
156
158
  i0.ɵɵtext(4);
157
159
  i0.ɵɵelementEnd();
158
- i0.ɵɵelementStart(5, "td", 47);
160
+ i0.ɵɵelementStart(5, "td", 49);
159
161
  i0.ɵɵtext(6);
160
162
  i0.ɵɵpipe(7, "number");
161
163
  i0.ɵɵelementEnd();
162
- i0.ɵɵelementStart(8, "td", 47);
164
+ i0.ɵɵelementStart(8, "td", 49);
163
165
  i0.ɵɵtext(9);
164
166
  i0.ɵɵpipe(10, "number");
165
167
  i0.ɵɵelementEnd();
166
- i0.ɵɵelementStart(11, "td", 47);
168
+ i0.ɵɵelementStart(11, "td", 49);
167
169
  i0.ɵɵtext(12);
168
170
  i0.ɵɵpipe(13, "number");
169
171
  i0.ɵɵelementEnd();
170
- i0.ɵɵelementStart(14, "td", 47);
172
+ i0.ɵɵelementStart(14, "td", 49);
171
173
  i0.ɵɵtext(15);
174
+ i0.ɵɵpipe(16, "number");
172
175
  i0.ɵɵelementEnd();
173
- i0.ɵɵelementStart(16, "td", 47);
174
- i0.ɵɵtext(17);
176
+ i0.ɵɵelementStart(17, "td", 49);
177
+ i0.ɵɵtext(18);
175
178
  i0.ɵɵelementEnd();
176
- i0.ɵɵelementStart(18, "td", 48);
177
- i0.ɵɵtext(19);
179
+ i0.ɵɵelementStart(19, "td", 49);
180
+ i0.ɵɵtext(20);
178
181
  i0.ɵɵelementEnd();
179
- i0.ɵɵelementStart(20, "td", 47);
180
- i0.ɵɵtext(21);
181
- i0.ɵɵpipe(22, "number");
182
+ i0.ɵɵelementStart(21, "td", 50);
183
+ i0.ɵɵtext(22);
184
+ i0.ɵɵelementEnd();
185
+ i0.ɵɵelementStart(23, "td", 49);
186
+ i0.ɵɵtext(24);
187
+ i0.ɵɵelementEnd();
188
+ i0.ɵɵelementStart(25, "td", 49);
189
+ i0.ɵɵtext(26);
190
+ i0.ɵɵpipe(27, "number");
182
191
  i0.ɵɵelementEnd()();
183
192
  } if (rf & 2) {
184
193
  const row_r6 = ctx.$implicit;
@@ -188,11 +197,13 @@ function AnalyticsCostBudgetComponent_Conditional_1_For_53_Template(rf, ctx) { i
188
197
  i0.ɵɵadvance(2);
189
198
  i0.ɵɵtextInterpolate(row_r6.Vendor);
190
199
  i0.ɵɵadvance(2);
191
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(7, 9, row_r6.Runs));
200
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(7, 11, row_r6.Runs));
192
201
  i0.ɵɵadvance(3);
193
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(10, 11, row_r6.InputTokens));
202
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(10, 13, row_r6.InputTokens));
194
203
  i0.ɵɵadvance(3);
195
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(13, 13, row_r6.OutputTokens));
204
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(13, 15, row_r6.OutputTokens));
205
+ i0.ɵɵadvance(3);
206
+ i0.ɵɵtextInterpolate(row_r6.CacheReadTokens + row_r6.CacheWriteTokens > 0 ? i0.ɵɵpipeBind2(16, 17, row_r6.CacheHitRate * 100, "1.0-0") + "%" : "\u2014");
196
207
  i0.ɵɵadvance(3);
197
208
  i0.ɵɵtextInterpolate(ctx_r2.FormatCurrency(row_r6.InputCost, 4));
198
209
  i0.ɵɵadvance(2);
@@ -200,7 +211,9 @@ function AnalyticsCostBudgetComponent_Conditional_1_For_53_Template(rf, ctx) { i
200
211
  i0.ɵɵadvance(2);
201
212
  i0.ɵɵtextInterpolate(ctx_r2.FormatCurrency(row_r6.TotalCost));
202
213
  i0.ɵɵadvance(2);
203
- i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(22, 15, row_r6.PercentOfTotal, "1.1-1"), "%");
214
+ i0.ɵɵtextInterpolate(row_r6.CacheSavings > 0 ? ctx_r2.FormatCurrency(row_r6.CacheSavings, 4) : "\u2014");
215
+ i0.ɵɵadvance(2);
216
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(27, 20, row_r6.PercentOfTotal, "1.1-1"), "%");
204
217
  } }
205
218
  function AnalyticsCostBudgetComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
206
219
  const _r1 = i0.ɵɵgetCurrentView();
@@ -245,21 +258,27 @@ function AnalyticsCostBudgetComponent_Conditional_1_Template(rf, ctx) { if (rf &
245
258
  i0.ɵɵelementStart(40, "th", 22);
246
259
  i0.ɵɵtext(41, "Output Tokens");
247
260
  i0.ɵɵelementEnd();
248
- i0.ɵɵelementStart(42, "th", 22);
249
- i0.ɵɵtext(43, "Input Cost");
261
+ i0.ɵɵelementStart(42, "th", 23);
262
+ i0.ɵɵtext(43, "Cache Hit");
250
263
  i0.ɵɵelementEnd();
251
264
  i0.ɵɵelementStart(44, "th", 22);
252
- i0.ɵɵtext(45, "Output Cost");
265
+ i0.ɵɵtext(45, "Input Cost");
253
266
  i0.ɵɵelementEnd();
254
267
  i0.ɵɵelementStart(46, "th", 22);
255
- i0.ɵɵtext(47, "Total Cost");
268
+ i0.ɵɵtext(47, "Output Cost");
256
269
  i0.ɵɵelementEnd();
257
270
  i0.ɵɵelementStart(48, "th", 22);
258
- i0.ɵɵtext(49, "% of Total");
271
+ i0.ɵɵtext(49, "Total Cost");
272
+ i0.ɵɵelementEnd();
273
+ i0.ɵɵelementStart(50, "th", 24);
274
+ i0.ɵɵtext(51, "Cache Saved");
275
+ i0.ɵɵelementEnd();
276
+ i0.ɵɵelementStart(52, "th", 22);
277
+ i0.ɵɵtext(53, "% of Total");
259
278
  i0.ɵɵelementEnd()()();
260
- i0.ɵɵelementStart(50, "tbody");
261
- i0.ɵɵconditionalCreate(51, AnalyticsCostBudgetComponent_Conditional_1_Conditional_51_Template, 3, 0, "tr");
262
- i0.ɵɵrepeaterCreate(52, AnalyticsCostBudgetComponent_Conditional_1_For_53_Template, 23, 18, "tr", null, _forTrack1);
279
+ i0.ɵɵelementStart(54, "tbody");
280
+ i0.ɵɵconditionalCreate(55, AnalyticsCostBudgetComponent_Conditional_1_Conditional_55_Template, 3, 0, "tr");
281
+ i0.ɵɵrepeaterCreate(56, AnalyticsCostBudgetComponent_Conditional_1_For_57_Template, 28, 23, "tr", null, _forTrack1);
263
282
  i0.ɵɵelementEnd()()()();
264
283
  } if (rf & 2) {
265
284
  const ctx_r2 = i0.ɵɵnextContext();
@@ -269,15 +288,21 @@ function AnalyticsCostBudgetComponent_Conditional_1_Template(rf, ctx) { if (rf &
269
288
  i0.ɵɵconditional(ctx_r2.DailyBars.length === 0 ? 10 : 11);
270
289
  i0.ɵɵadvance(8);
271
290
  i0.ɵɵconditional(ctx_r2.TreemapCells.length === 0 ? 18 : 19);
272
- i0.ɵɵadvance(33);
273
- i0.ɵɵconditional(ctx_r2.CostByModelRows.length === 0 ? 51 : -1);
291
+ i0.ɵɵadvance(37);
292
+ i0.ɵɵconditional(ctx_r2.CostByModelRows.length === 0 ? 55 : -1);
274
293
  i0.ɵɵadvance();
275
294
  i0.ɵɵrepeater(ctx_r2.CostByModelRows);
276
295
  } }
277
296
  const FIELDS = [
278
297
  'ID', 'RunAt', 'Cost', 'TotalCost', 'TokensPrompt', 'TokensCompletion',
279
- 'TokensUsed', 'ModelID', 'Model', 'VendorID', 'Vendor', 'Success'
298
+ 'TokensUsed', 'TokensCacheRead', 'TokensCacheWrite', 'ModelID', 'Model', 'VendorID', 'Vendor', 'Success'
280
299
  ];
300
+ // AIModelCost UnitType name -> token divisor, matching the BasePriceUnitType driver classes.
301
+ const UNIT_DIVISORS = {
302
+ 'Per Million Tokens': 1_000_000,
303
+ 'Per Hundred Thousand Tokens': 100_000,
304
+ 'Per Thousand Tokens': 1_000
305
+ };
281
306
  const TIME_RANGE_OPTIONS = ['Today', '7d', '30d', 'MTD'];
282
307
  const TREEMAP_COLORS = [
283
308
  'var(--mj-brand-primary)',
@@ -323,6 +348,9 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
323
348
  CostByModelRows = [];
324
349
  allRuns = [];
325
350
  previousPeriodRuns = [];
351
+ // Per model+vendor cache pricing (rates already normalized to currency-per-token). Empty until
352
+ // AIModelCost cache rates are configured — savings then stays 0 (surfaced as "Set rates").
353
+ cacheRates = new Map();
326
354
  ngOnInit() {
327
355
  this.initialized = true;
328
356
  this.LoadData();
@@ -350,8 +378,8 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
350
378
  return '$' + value.toFixed(decimals);
351
379
  }
352
380
  ExportCSV() {
353
- const header = 'Model,Vendor,Runs,Input Tokens,Output Tokens,Input Cost,Output Cost,Total Cost,% of Total';
354
- const rows = this.CostByModelRows.map(r => `"${r.Model}","${r.Vendor}",${r.Runs},${r.InputTokens},${r.OutputTokens},${r.InputCost.toFixed(6)},${r.OutputCost.toFixed(6)},${r.TotalCost.toFixed(6)},${r.PercentOfTotal.toFixed(1)}`);
381
+ const header = 'Model,Vendor,Runs,Input Tokens,Output Tokens,Cache Read Tokens,Cache Write Tokens,Cache Hit Rate %,Input Cost,Output Cost,Total Cost,Cache Saved,% of Total';
382
+ const rows = this.CostByModelRows.map(r => `"${r.Model}","${r.Vendor}",${r.Runs},${r.InputTokens},${r.OutputTokens},${r.CacheReadTokens},${r.CacheWriteTokens},${(r.CacheHitRate * 100).toFixed(1)},${r.InputCost.toFixed(6)},${r.OutputCost.toFixed(6)},${r.TotalCost.toFixed(6)},${r.CacheSavings.toFixed(6)},${r.PercentOfTotal.toFixed(1)}`);
355
383
  const csv = [header, ...rows].join('\n');
356
384
  this.downloadCSV(csv, 'cost-by-model.csv');
357
385
  }
@@ -366,7 +394,7 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
366
394
  const modelFilter = this.buildModelFilter();
367
395
  const currentFilter = this.combineDateAndModelFilter(currentStart, now, modelFilter);
368
396
  const prevFilter = this.combineDateAndModelFilter(previousStart, currentStart, modelFilter);
369
- const [currentResult, prevResult] = await rv.RunViews([
397
+ const [currentResult, prevResult, rateResult] = await rv.RunViews([
370
398
  {
371
399
  EntityName: 'MJ: AI Prompt Runs',
372
400
  ExtraFilter: currentFilter,
@@ -380,10 +408,17 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
380
408
  Fields: FIELDS,
381
409
  OrderBy: 'RunAt ASC',
382
410
  ResultType: 'simple'
411
+ },
412
+ {
413
+ EntityName: 'MJ: AI Model Costs',
414
+ ExtraFilter: `Status='Active' AND ProcessingType='Realtime'`,
415
+ Fields: ['ModelID', 'VendorID', 'InputPricePerUnit', 'OutputPricePerUnit', 'CacheReadPricePerUnit', 'CacheWritePricePerUnit', 'UnitType'],
416
+ ResultType: 'simple'
383
417
  }
384
418
  ]);
385
419
  this.allRuns = (currentResult?.Results ?? []);
386
420
  this.previousPeriodRuns = (prevResult?.Results ?? []);
421
+ this.buildCacheRateMap(rateResult?.Results ?? []);
387
422
  this.computeKpis();
388
423
  this.computeDailyBars();
389
424
  this.computeTreemap();
@@ -397,6 +432,36 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
397
432
  this.cdr.detectChanges();
398
433
  }
399
434
  }
435
+ // ── Cache pricing ──
436
+ /** Stable map key for a model+vendor pair (UUIDs normalized for case-insensitive matching). */
437
+ rateKey(modelID, vendorID) {
438
+ return `${NormalizeUUID(modelID ?? '')}|${NormalizeUUID(vendorID ?? '')}`;
439
+ }
440
+ /** The cache rate for a run's model+vendor, or undefined when no active cost row is configured. */
441
+ rateFor(run) {
442
+ return this.cacheRates.get(this.rateKey(run.ModelID, run.VendorID));
443
+ }
444
+ /** Build the per-model+vendor rate lookup, normalizing each per-unit price to currency-per-token. */
445
+ buildCacheRateMap(rows) {
446
+ this.cacheRates.clear();
447
+ for (const row of rows) {
448
+ const divisor = UNIT_DIVISORS[row.UnitType ?? ''] ?? 1_000_000;
449
+ const inputRate = (row.InputPricePerUnit ?? 0) / divisor;
450
+ // Cache read/write fall back to the input rate when no distinct rate is recorded — exactly
451
+ // as the server-side cost calculator does — which makes the corresponding savings term 0.
452
+ const cacheReadRate = (row.CacheReadPricePerUnit ?? row.InputPricePerUnit ?? 0) / divisor;
453
+ const cacheWriteRate = (row.CacheWritePricePerUnit ?? row.InputPricePerUnit ?? 0) / divisor;
454
+ this.cacheRates.set(this.rateKey(row.ModelID, row.VendorID), { inputRate, cacheReadRate, cacheWriteRate });
455
+ }
456
+ }
457
+ /** Sum net cache savings across a set of runs using each run's model+vendor rate. */
458
+ sumCacheSavings(runs) {
459
+ return runs.reduce((total, run) => total + netCacheSavings({
460
+ uncachedInputTokens: 0,
461
+ cacheReadTokens: run.TokensCacheRead ?? 0,
462
+ cacheWriteTokens: run.TokensCacheWrite ?? 0
463
+ }, this.rateFor(run)), 0);
464
+ }
400
465
  // ── Computations ──
401
466
  computeKpis() {
402
467
  const now = new Date();
@@ -446,6 +511,39 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
446
511
  Icon: 'fa-solid fa-chart-line'
447
512
  }
448
513
  ];
514
+ this.appendCacheKpis();
515
+ }
516
+ /** Append the cache hit-rate and cache-savings KPIs (computed from the current-period runs). */
517
+ appendCacheKpis() {
518
+ const totals = { uncachedInputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 };
519
+ for (const r of this.allRuns) {
520
+ totals.uncachedInputTokens += r.TokensPrompt ?? 0;
521
+ totals.cacheReadTokens += r.TokensCacheRead ?? 0;
522
+ totals.cacheWriteTokens += r.TokensCacheWrite ?? 0;
523
+ }
524
+ const savings = this.sumCacheSavings(this.allRuns);
525
+ const activity = hasCacheActivity(totals);
526
+ this.CostKpis.push({
527
+ Label: 'Cache Hit Rate',
528
+ Value: (cacheHitRate(totals) * 100).toFixed(1) + '%',
529
+ Delta: null,
530
+ DeltaDirection: 'stable',
531
+ Highlighted: false,
532
+ Icon: 'fa-solid fa-bolt'
533
+ });
534
+ // Savings requires cache rates on AIModelCost. When cache engaged but no savings computed,
535
+ // it means rates aren't configured yet — say so rather than implying $0 was saved.
536
+ const savingsValue = savings > 0
537
+ ? this.FormatCurrency(savings)
538
+ : (activity ? 'Set rates' : '$0.00');
539
+ this.CostKpis.push({
540
+ Label: 'Saved via Cache',
541
+ Value: savingsValue,
542
+ Delta: null,
543
+ DeltaDirection: 'stable',
544
+ Highlighted: savings > 0,
545
+ Icon: 'fa-solid fa-piggy-bank'
546
+ });
449
547
  }
450
548
  computeDailyBars() {
451
549
  const buckets = new Map();
@@ -502,6 +600,8 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
502
600
  for (const [, modelRuns] of groups) {
503
601
  const inputTokens = modelRuns.reduce((s, r) => s + (r.TokensPrompt ?? 0), 0);
504
602
  const outputTokens = modelRuns.reduce((s, r) => s + (r.TokensCompletion ?? 0), 0);
603
+ const cacheReadTokens = modelRuns.reduce((s, r) => s + (r.TokensCacheRead ?? 0), 0);
604
+ const cacheWriteTokens = modelRuns.reduce((s, r) => s + (r.TokensCacheWrite ?? 0), 0);
505
605
  const cost = modelRuns.reduce((s, r) => s + (r.Cost ?? r.TotalCost ?? 0), 0);
506
606
  // Approximate input/output cost split based on token ratio
507
607
  const totalTk = inputTokens + outputTokens;
@@ -513,6 +613,10 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
513
613
  Runs: modelRuns.length,
514
614
  InputTokens: inputTokens,
515
615
  OutputTokens: outputTokens,
616
+ CacheReadTokens: cacheReadTokens,
617
+ CacheWriteTokens: cacheWriteTokens,
618
+ CacheHitRate: cacheHitRate({ uncachedInputTokens: inputTokens, cacheReadTokens, cacheWriteTokens }),
619
+ CacheSavings: this.sumCacheSavings(modelRuns),
516
620
  InputCost: inputCost,
517
621
  OutputCost: outputCost,
518
622
  TotalCost: cost,
@@ -573,8 +677,8 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
573
677
  URL.revokeObjectURL(url);
574
678
  }
575
679
  static ɵfac = /*@__PURE__*/ (() => { let ɵAnalyticsCostBudgetComponent_BaseFactory; return function AnalyticsCostBudgetComponent_Factory(__ngFactoryType__) { return (ɵAnalyticsCostBudgetComponent_BaseFactory || (ɵAnalyticsCostBudgetComponent_BaseFactory = i0.ɵɵgetInheritedFactory(AnalyticsCostBudgetComponent)))(__ngFactoryType__ || AnalyticsCostBudgetComponent); }; })();
576
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AnalyticsCostBudgetComponent, selectors: [["app-analytics-cost-budget"]], inputs: { TimeRange: "TimeRange", Filters: "Filters" }, outputs: { TimeRangeChange: "TimeRangeChange", FiltersChange: "FiltersChange" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "loading-container"], ["text", "Loading cost data..."], [1, "kpi-row"], [1, "kpi-card", 3, "kpi-card--highlighted"], [1, "two-col"], [1, "panel", "panel-chart"], [1, "panel-header"], [1, "panel-header__title"], [1, "fa-solid", "fa-chart-column", "panel-header__icon"], [1, "chart-body"], [1, "panel-empty"], [1, "bar-chart"], [1, "panel", "panel-treemap"], [1, "fa-solid", "fa-chart-pie", "panel-header__icon"], [1, "treemap-body"], [1, "treemap-grid"], [1, "panel"], [1, "fa-solid", "fa-table", "panel-header__icon"], [1, "export-btn", 3, "click"], [1, "fa-solid", "fa-download"], [1, "table-wrapper"], [1, "data-table"], [1, "col-numeric"], [1, "kpi-card"], [1, "kpi-icon"], [1, "kpi-content"], [1, "kpi-label"], [1, "kpi-value"], [1, "kpi-delta", 3, "kpi-delta--up", "kpi-delta--down"], [1, "kpi-delta"], [1, "fa-solid", "fa-arrow-up"], [1, "fa-solid", "fa-arrow-down"], [1, "bar-chart-area"], [1, "avg-line", 3, "bottom"], [1, "bar-col", 3, "title"], [1, "avg-line"], [1, "avg-label"], [1, "bar"], [1, "bar-label"], [1, "treemap-cell", 3, "background", "flex-basis", "title"], [1, "treemap-cell", 3, "title"], [1, "treemap-label"], [1, "treemap-value"], [1, "treemap-pct"], ["colspan", "9", 1, "empty-row"], [1, "cell-model"], [1, "cell-vendor"], [1, "cell-numeric"], [1, "cell-numeric", "cell-cost"]], template: function AnalyticsCostBudgetComponent_Template(rf, ctx) { if (rf & 1) {
577
- i0.ɵɵconditionalCreate(0, AnalyticsCostBudgetComponent_Conditional_0_Template, 2, 0, "div", 0)(1, AnalyticsCostBudgetComponent_Conditional_1_Template, 54, 3);
680
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AnalyticsCostBudgetComponent, selectors: [["app-analytics-cost-budget"]], inputs: { TimeRange: "TimeRange", Filters: "Filters" }, outputs: { TimeRangeChange: "TimeRangeChange", FiltersChange: "FiltersChange" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "loading-container"], ["text", "Loading cost data..."], [1, "kpi-row"], [1, "kpi-card", 3, "kpi-card--highlighted"], [1, "two-col"], [1, "panel", "panel-chart"], [1, "panel-header"], [1, "panel-header__title"], [1, "fa-solid", "fa-chart-column", "panel-header__icon"], [1, "chart-body"], [1, "panel-empty"], [1, "bar-chart"], [1, "panel", "panel-treemap"], [1, "fa-solid", "fa-chart-pie", "panel-header__icon"], [1, "treemap-body"], [1, "treemap-grid"], [1, "panel"], [1, "fa-solid", "fa-table", "panel-header__icon"], [1, "export-btn", 3, "click"], [1, "fa-solid", "fa-download"], [1, "table-wrapper"], [1, "data-table"], [1, "col-numeric"], ["title", "Share of input tokens served from the provider's prompt cache", 1, "col-numeric"], ["title", "Net dollars saved by caching vs. full input pricing (requires cache rates on AI Model Costs)", 1, "col-numeric"], [1, "kpi-card"], [1, "kpi-icon"], [1, "kpi-content"], [1, "kpi-label"], [1, "kpi-value"], [1, "kpi-delta", 3, "kpi-delta--up", "kpi-delta--down"], [1, "kpi-delta"], [1, "fa-solid", "fa-arrow-up"], [1, "fa-solid", "fa-arrow-down"], [1, "bar-chart-area"], [1, "avg-line", 3, "bottom"], [1, "bar-col", 3, "title"], [1, "avg-line"], [1, "avg-label"], [1, "bar"], [1, "bar-label"], [1, "treemap-cell", 3, "background", "flex-basis", "title"], [1, "treemap-cell", 3, "title"], [1, "treemap-label"], [1, "treemap-value"], [1, "treemap-pct"], ["colspan", "11", 1, "empty-row"], [1, "cell-model"], [1, "cell-vendor"], [1, "cell-numeric"], [1, "cell-numeric", "cell-cost"]], template: function AnalyticsCostBudgetComponent_Template(rf, ctx) { if (rf & 1) {
681
+ i0.ɵɵconditionalCreate(0, AnalyticsCostBudgetComponent_Conditional_0_Template, 2, 0, "div", 0)(1, AnalyticsCostBudgetComponent_Conditional_1_Template, 58, 3);
578
682
  } if (rf & 2) {
579
683
  i0.ɵɵconditional(ctx.IsLoading ? 0 : 1);
580
684
  } }, dependencies: [i1.LoadingComponent, i2.DecimalPipe], styles: ["[_nghost-%COMP%] { display: block; }\n\n .loading-container[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 300px;\n }\n\n \n\n .kpi-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 12px;\n margin: 16px 0;\n }\n\n .kpi-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n padding: 16px;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n\n .kpi-card[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n }\n\n .kpi-card--highlighted[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n }\n\n .kpi-icon[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 14px;\n flex-shrink: 0;\n }\n\n .kpi-card--highlighted[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-bg-surface));\n }\n\n .kpi-label[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .kpi-value[_ngcontent-%COMP%] {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 2px 0;\n letter-spacing: -0.02em;\n }\n\n .kpi-delta[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 3px;\n }\n\n .kpi-delta--up[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n }\n\n .kpi-delta--down[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n }\n\n \n\n .two-col[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 3fr 2fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n \n\n .panel[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n overflow: hidden;\n margin-bottom: 16px;\n }\n\n .panel-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 18px;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .panel-header__title[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .panel-header__icon[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-brand-primary);\n }\n\n .panel-empty[_ngcontent-%COMP%] {\n text-align: center;\n padding: 32px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .export-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 13px;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n }\n\n .export-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n }\n\n \n\n .chart-body[_ngcontent-%COMP%] {\n padding: 16px 18px;\n }\n\n .bar-chart[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .bar-chart-area[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n gap: 4px;\n height: 180px;\n position: relative;\n padding-bottom: 24px;\n }\n\n .bar-col[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n height: 100%;\n justify-content: flex-end;\n }\n\n .bar[_ngcontent-%COMP%] {\n width: 100%;\n max-width: 32px;\n border-radius: 4px 4px 0 0;\n background: var(--mj-brand-primary);\n transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 2px;\n }\n\n .bar--anomaly[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n }\n\n .bar-label[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-muted);\n margin-top: 4px;\n white-space: nowrap;\n }\n\n .avg-line[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n right: 0;\n border-top: 2px dashed var(--mj-text-disabled);\n z-index: 1;\n pointer-events: none;\n }\n\n .avg-label[_ngcontent-%COMP%] {\n position: absolute;\n right: 0;\n top: -14px;\n font-size: 10px;\n color: var(--mj-text-disabled);\n font-weight: 600;\n }\n\n \n\n .treemap-body[_ngcontent-%COMP%] {\n padding: 16px 18px;\n }\n\n .treemap-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n min-height: 160px;\n }\n\n .treemap-cell[_ngcontent-%COMP%] {\n border-radius: 8px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n justify-content: center;\n min-width: 80px;\n min-height: 70px;\n flex-grow: 1;\n color: var(--mj-text-inverse, white);\n transition: opacity 0.2s;\n }\n\n .treemap-cell[_ngcontent-%COMP%]:hover {\n opacity: 0.85;\n }\n\n .treemap-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n opacity: 0.95;\n }\n\n .treemap-value[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 700;\n margin-top: 2px;\n }\n\n .treemap-pct[_ngcontent-%COMP%] {\n font-size: 11px;\n opacity: 0.8;\n }\n\n \n\n .table-wrapper[_ngcontent-%COMP%] {\n overflow-x: auto;\n }\n\n .data-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n\n .data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n .data-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 14px;\n text-align: left;\n border-bottom: 1px solid var(--mj-border-subtle);\n }\n\n .data-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.4px;\n background: var(--mj-bg-surface-card);\n position: sticky;\n top: 0;\n white-space: nowrap;\n }\n\n .col-numeric[_ngcontent-%COMP%] { text-align: right; }\n\n .data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%] {\n transition: background 0.15s;\n }\n\n .data-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n }\n\n .cell-model[_ngcontent-%COMP%] {\n font-weight: 500;\n color: var(--mj-text-primary);\n }\n\n .cell-vendor[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n }\n\n .cell-numeric[_ngcontent-%COMP%] {\n text-align: right;\n font-variant-numeric: tabular-nums;\n color: var(--mj-text-secondary);\n }\n\n .cell-cost[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .empty-row[_ngcontent-%COMP%] {\n text-align: center;\n color: var(--mj-text-disabled);\n padding: 24px;\n }\n\n \n\n @media (max-width: 1200px) {\n .two-col[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 768px) {\n .kpi-row[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .kpi-value[_ngcontent-%COMP%] {\n font-size: 18px;\n }\n }"] });
@@ -703,15 +807,17 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
703
807
  <th class="col-numeric">Runs</th>
704
808
  <th class="col-numeric">Input Tokens</th>
705
809
  <th class="col-numeric">Output Tokens</th>
810
+ <th class="col-numeric" title="Share of input tokens served from the provider's prompt cache">Cache Hit</th>
706
811
  <th class="col-numeric">Input Cost</th>
707
812
  <th class="col-numeric">Output Cost</th>
708
813
  <th class="col-numeric">Total Cost</th>
814
+ <th class="col-numeric" title="Net dollars saved by caching vs. full input pricing (requires cache rates on AI Model Costs)">Cache Saved</th>
709
815
  <th class="col-numeric">% of Total</th>
710
816
  </tr>
711
817
  </thead>
712
818
  <tbody>
713
819
  @if (CostByModelRows.length === 0) {
714
- <tr><td colspan="9" class="empty-row">No data available</td></tr>
820
+ <tr><td colspan="11" class="empty-row">No data available</td></tr>
715
821
  }
716
822
  @for (row of CostByModelRows; track row.Model) {
717
823
  <tr>
@@ -720,9 +826,11 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
720
826
  <td class="cell-numeric">{{ row.Runs | number }}</td>
721
827
  <td class="cell-numeric">{{ row.InputTokens | number }}</td>
722
828
  <td class="cell-numeric">{{ row.OutputTokens | number }}</td>
829
+ <td class="cell-numeric">{{ (row.CacheReadTokens + row.CacheWriteTokens) > 0 ? ((row.CacheHitRate * 100 | number:'1.0-0') + '%') : '—' }}</td>
723
830
  <td class="cell-numeric">{{ FormatCurrency(row.InputCost, 4) }}</td>
724
831
  <td class="cell-numeric">{{ FormatCurrency(row.OutputCost, 4) }}</td>
725
832
  <td class="cell-numeric cell-cost">{{ FormatCurrency(row.TotalCost) }}</td>
833
+ <td class="cell-numeric">{{ row.CacheSavings > 0 ? FormatCurrency(row.CacheSavings, 4) : '—' }}</td>
726
834
  <td class="cell-numeric">{{ row.PercentOfTotal | number:'1.1-1' }}%</td>
727
835
  </tr>
728
836
  }
@@ -741,6 +849,6 @@ export class AnalyticsCostBudgetComponent extends BaseAngularComponent {
741
849
  }], FiltersChange: [{
742
850
  type: Output
743
851
  }] }); })();
744
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AnalyticsCostBudgetComponent, { className: "AnalyticsCostBudgetComponent", filePath: "src/AI/components/analytics/cost-budget/cost-budget.component.ts", lineNumber: 589 }); })();
852
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AnalyticsCostBudgetComponent, { className: "AnalyticsCostBudgetComponent", filePath: "src/AI/components/analytics/cost-budget/cost-budget.component.ts", lineNumber: 618 }); })();
745
853
  export function LoadAnalyticsCostBudget() { }
746
854
  //# sourceMappingURL=cost-budget.component.js.map