@oneuptime/common 12.0.23 → 12.0.25
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.
- package/Models/AnalyticsModels/Span.ts +101 -0
- package/Models/DatabaseModels/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +27 -7
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +67 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/BaseAPI.ts +0 -24
- package/Server/API/SlackAPI.ts +0 -2
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Middleware/SlackAuthorization.ts +96 -18
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +205 -0
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
- package/Server/Services/NetworkDeviceService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
- package/Server/Services/ProjectService.ts +29 -19
- package/Server/Services/SecurityEventService.ts +90 -13
- package/Server/Services/StatusPageResourceService.ts +223 -0
- package/Server/Services/TelemetryAttributeService.ts +17 -0
- package/Server/Services/UserService.ts +4 -6
- package/Server/Types/Markdown.ts +161 -67
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
- package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
- package/Server/Utils/Attribution.ts +4 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
- package/Server/Utils/StartServer.ts +12 -32
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
- package/Server/Utils/Telemetry/LlmMetricSpend.ts +181 -0
- package/Server/Utils/Telemetry/LlmSpan.ts +46 -0
- package/Server/Utils/Workspace/Slack/Actions/Auth.ts +0 -12
- package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
- package/Server/Views/Partials/Head.ejs +2 -1
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
- package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
- package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
- package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
- package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
- package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
- package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
- package/Tests/App/Dashboard/LlmCallsTableIdentity.test.tsx +322 -0
- package/Tests/App/Dashboard/LlmOverview.test.tsx +335 -0
- package/Tests/App/Dashboard/LlmSpanDisplay.test.ts +391 -0
- package/Tests/App/Dashboard/LlmUsageBreakdown.test.tsx +1007 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
- package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
- package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
- package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
- package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
- package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
- package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
- package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
- package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
- package/Tests/Server/API/BaseAPI.test.ts +41 -0
- package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +9 -16
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
- package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
- package/Tests/Server/Middleware/SlackAuthorization.test.ts +262 -5
- package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
- package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
- package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
- package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
- package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +387 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +386 -0
- package/Tests/Server/Utils/Telemetry/LlmSpan.test.ts +804 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
- package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
- package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
- package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
- package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +584 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
- package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
- package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
- package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
- package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
- package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
- package/Tests/UI/Components/SideOver.test.tsx +1 -1
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
- package/Tests/UI/Components/Toggle.test.tsx +25 -0
- package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +770 -0
- package/Types/Code/YamlSyntax.ts +396 -0
- package/Types/Exception/ExceptionCode.ts +1 -0
- package/Types/Exception/PayloadTooLargeException.ts +8 -0
- package/Types/Marketing/Attribution.ts +10 -0
- package/Types/Marketing/MarketingEvent.ts +4 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmConventions.ts +255 -0
- package/Types/Telemetry/LlmMetricConventions.ts +329 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
- package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
- package/UI/Components/Detail/Detail.tsx +10 -0
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
- package/UI/Components/Forms/Fields/FormField.tsx +31 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
- package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
- package/UI/Components/Forms/Validation.ts +46 -0
- package/UI/Components/Input/Input.tsx +3 -0
- package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
- package/UI/Components/ModelTable/Column.ts +8 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
- package/UI/Components/SideOver/SideOver.tsx +5 -5
- package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
- package/UI/Components/StatusPage/ResourceList.tsx +197 -38
- package/UI/Components/Toggle/Toggle.tsx +9 -1
- package/UI/Components/Types/FieldType.ts +1 -0
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +287 -0
- package/build/dist/Models/AnalyticsModels/Span.js +89 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +27 -7
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +69 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +76 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +76 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +3 -19
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +0 -2
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
- package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
- package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js +58 -7
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +204 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -45
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +57 -5
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
- package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +2 -12
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +147 -61
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -21
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +151 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js +24 -1
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +0 -10
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
- package/build/dist/Types/Code/YamlSyntax.js +212 -0
- package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
- package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
- package/build/dist/Types/Marketing/Attribution.js +10 -0
- package/build/dist/Types/Marketing/Attribution.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmConventions.js +236 -0
- package/build/dist/Types/Telemetry/LlmConventions.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmMetricConventions.js +290 -0
- package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +9 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +27 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
- package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Types/FieldType.js +1 -0
- package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js +175 -0
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +2 -1
- package/Tests/UI/Components/Toast.test.tsx +0 -197
- package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
- package/UI/Components/Toast/Toast.tsx +0 -189
- package/UI/Components/Toast/ToastInit.tsx +0 -110
- package/build/dist/UI/Components/Toast/Toast.js +0 -123
- package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
- package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
|
@@ -19,6 +19,8 @@ import LlmCostBudgetService from "../../../../Server/Services/LlmCostBudgetServi
|
|
|
19
19
|
import MetricService from "../../../../Server/Services/MetricService";
|
|
20
20
|
import SpanService from "../../../../Server/Services/SpanService";
|
|
21
21
|
import TelemetryUtil from "../../../../Server/Utils/Telemetry/Telemetry";
|
|
22
|
+
import { LlmMicroUsdCostMetricNames } from "../../../../Types/Telemetry/LlmMetricConventions";
|
|
23
|
+
import { LlmMetricScope } from "../../../../Utils/Telemetry/LlmMetricQuery";
|
|
22
24
|
/*
|
|
23
25
|
* `jest` deliberately comes from the global scope, not @jest/globals — the
|
|
24
26
|
* imported value would shadow the global `jest` NAMESPACE and break the
|
|
@@ -41,6 +43,11 @@ jest.mock("../../../../Server/Services/MetricService", () => {
|
|
|
41
43
|
__esModule: true,
|
|
42
44
|
default: {
|
|
43
45
|
insertJsonRows: jest.fn(),
|
|
46
|
+
/*
|
|
47
|
+
* aggregateBy backs the metric-sourced spend fallback that
|
|
48
|
+
* resolveSpend reaches for when the span stream is empty.
|
|
49
|
+
*/
|
|
50
|
+
aggregateBy: jest.fn(),
|
|
44
51
|
},
|
|
45
52
|
};
|
|
46
53
|
});
|
|
@@ -70,10 +77,41 @@ const mockedSpanService: { aggregateBy: MockedFn } = SpanService as unknown as {
|
|
|
70
77
|
aggregateBy: MockedFn;
|
|
71
78
|
};
|
|
72
79
|
|
|
73
|
-
const mockedMetricService: {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
const mockedMetricService: {
|
|
81
|
+
insertJsonRows: MockedFn;
|
|
82
|
+
aggregateBy: MockedFn;
|
|
83
|
+
} = MetricService as unknown as {
|
|
84
|
+
insertJsonRows: MockedFn;
|
|
85
|
+
aggregateBy: MockedFn;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* Metric-sourced cost is TWO aggregates, not one: the USD-denominated metric
|
|
90
|
+
* names and the micro-USD ones (the Codex CLI reports millionths of a dollar)
|
|
91
|
+
* are queried separately and scaled before being added, because a single Sum
|
|
92
|
+
* over both units cannot be un-mixed afterwards. A plain mockResolvedValue
|
|
93
|
+
* answers BOTH queries with the same payload, which would feed the same
|
|
94
|
+
* figure in twice — once at 1e-6 — and quietly skew every expectation here.
|
|
95
|
+
*
|
|
96
|
+
* So the mock dispatches on the metric names the query actually selects: the
|
|
97
|
+
* rows are the project's USD spend, and the micro-USD query correctly finds
|
|
98
|
+
* nothing.
|
|
99
|
+
*/
|
|
100
|
+
function mockUsdMetricRows(rows: Array<JSONObject>): void {
|
|
101
|
+
mockedMetricService.aggregateBy.mockImplementation(
|
|
102
|
+
(aggregate: { query: Record<string, unknown> }): Promise<JSONObject> => {
|
|
103
|
+
const names: Array<string> = ((
|
|
104
|
+
aggregate?.query?.["name"] as { values?: Array<string> } | undefined
|
|
105
|
+
)?.values || []) as Array<string>;
|
|
106
|
+
|
|
107
|
+
const isMicroUsd: boolean = names.some((name: string) => {
|
|
108
|
+
return LlmMicroUsdCostMetricNames.includes(name);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return Promise.resolve({ data: isMicroUsd ? [] : rows });
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
}
|
|
77
115
|
|
|
78
116
|
const mockedBudgetService: {
|
|
79
117
|
findBy: MockedFn;
|
|
@@ -328,6 +366,7 @@ describe("LlmCostBudgetEvaluator.evaluateBudget — orchestration", () => {
|
|
|
328
366
|
beforeEach(() => {
|
|
329
367
|
jest.clearAllMocks();
|
|
330
368
|
mockedSpanService.aggregateBy.mockResolvedValue({ data: [] } as never);
|
|
369
|
+
mockedMetricService.aggregateBy.mockResolvedValue({ data: [] } as never);
|
|
331
370
|
mockedMetricService.insertJsonRows.mockResolvedValue(undefined as never);
|
|
332
371
|
mockedBudgetService.updateOneById.mockResolvedValue(undefined as never);
|
|
333
372
|
mockedTelemetryUtil.indexMetricNameServiceNameMap.mockResolvedValue(
|
|
@@ -447,6 +486,7 @@ describe("LlmCostBudgetEvaluator.evaluateBudget — orchestration", () => {
|
|
|
447
486
|
describe("LlmCostBudgetEvaluator.evaluateAllBudgets — sweep resilience", () => {
|
|
448
487
|
beforeEach(() => {
|
|
449
488
|
jest.clearAllMocks();
|
|
489
|
+
mockedMetricService.aggregateBy.mockResolvedValue({ data: [] } as never);
|
|
450
490
|
mockedMetricService.insertJsonRows.mockResolvedValue(undefined as never);
|
|
451
491
|
mockedBudgetService.updateOneById.mockResolvedValue(undefined as never);
|
|
452
492
|
mockedTelemetryUtil.indexMetricNameServiceNameMap.mockResolvedValue(
|
|
@@ -481,6 +521,7 @@ describe("LlmCostBudgetEvaluator.evaluateAllBudgets — sweep resilience", () =>
|
|
|
481
521
|
await LlmCostBudgetEvaluator.evaluateAllBudgets();
|
|
482
522
|
|
|
483
523
|
expect(mockedSpanService.aggregateBy).not.toHaveBeenCalled();
|
|
524
|
+
expect(mockedMetricService.aggregateBy).not.toHaveBeenCalled();
|
|
484
525
|
});
|
|
485
526
|
|
|
486
527
|
test("only enabled budgets are loaded", async () => {
|
|
@@ -495,3 +536,345 @@ describe("LlmCostBudgetEvaluator.evaluateAllBudgets — sweep resilience", () =>
|
|
|
495
536
|
expect(findArg.query["isEnabled"]).toBe(true);
|
|
496
537
|
});
|
|
497
538
|
});
|
|
539
|
+
|
|
540
|
+
describe("LlmCostBudgetEvaluator.buildMetricScope", () => {
|
|
541
|
+
const DAY_START: Date = new Date(Date.UTC(2026, 7, 22, 0, 0, 0));
|
|
542
|
+
|
|
543
|
+
test("carries the project and the day-so-far window", () => {
|
|
544
|
+
const budget: LlmCostBudget = makeBudget();
|
|
545
|
+
|
|
546
|
+
const scope: LlmMetricScope = LlmCostBudgetEvaluator.buildMetricScope({
|
|
547
|
+
budget,
|
|
548
|
+
dayStart: DAY_START,
|
|
549
|
+
now: NOW,
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
expect(scope.projectId).toBe(budget.projectId);
|
|
553
|
+
expect(scope.startTime).toBe(DAY_START);
|
|
554
|
+
expect(scope.endTime).toBe(NOW);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
test("leaves an unscoped budget unscoped — it must see every datapoint", () => {
|
|
558
|
+
const scope: LlmMetricScope = LlmCostBudgetEvaluator.buildMetricScope({
|
|
559
|
+
budget: makeBudget(),
|
|
560
|
+
dayStart: DAY_START,
|
|
561
|
+
now: NOW,
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
expect(scope.serviceId).toBeUndefined();
|
|
565
|
+
expect(scope.llmSystem).toBeUndefined();
|
|
566
|
+
expect(scope.llmModel).toBeUndefined();
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test("mirrors the budget's service / provider / model scoping", () => {
|
|
570
|
+
const serviceId: ObjectID = ObjectID.generate();
|
|
571
|
+
|
|
572
|
+
const scope: LlmMetricScope = LlmCostBudgetEvaluator.buildMetricScope({
|
|
573
|
+
budget: makeBudget({
|
|
574
|
+
serviceId: serviceId,
|
|
575
|
+
llmSystem: "anthropic",
|
|
576
|
+
llmModel: "claude-sonnet-4",
|
|
577
|
+
}),
|
|
578
|
+
dayStart: DAY_START,
|
|
579
|
+
now: NOW,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
expect(scope.serviceId).toBe(serviceId);
|
|
583
|
+
expect(scope.llmSystem).toBe("anthropic");
|
|
584
|
+
expect(scope.llmModel).toBe("claude-sonnet-4");
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
/*
|
|
588
|
+
* The span query and the metric query must narrow to the same slice of
|
|
589
|
+
* traffic, or the fallback would silently measure something else.
|
|
590
|
+
*/
|
|
591
|
+
test("agrees with buildSpanQuery on the service scope", () => {
|
|
592
|
+
const serviceId: ObjectID = ObjectID.generate();
|
|
593
|
+
const budget: LlmCostBudget = makeBudget({ serviceId: serviceId });
|
|
594
|
+
|
|
595
|
+
const spanQuery: Record<string, unknown> =
|
|
596
|
+
LlmCostBudgetEvaluator.buildSpanQuery({
|
|
597
|
+
budget,
|
|
598
|
+
dayStart: DAY_START,
|
|
599
|
+
now: NOW,
|
|
600
|
+
}) as unknown as Record<string, unknown>;
|
|
601
|
+
|
|
602
|
+
const scope: LlmMetricScope = LlmCostBudgetEvaluator.buildMetricScope({
|
|
603
|
+
budget,
|
|
604
|
+
dayStart: DAY_START,
|
|
605
|
+
now: NOW,
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
expect(spanQuery["primaryEntityId"]).toBe(scope.serviceId);
|
|
609
|
+
});
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
describe("LlmCostBudgetEvaluator.resolveSpend — source selection", () => {
|
|
613
|
+
const DAY_START: Date = new Date(Date.UTC(2026, 7, 22, 0, 0, 0));
|
|
614
|
+
|
|
615
|
+
beforeEach(() => {
|
|
616
|
+
jest.clearAllMocks();
|
|
617
|
+
mockedSpanService.aggregateBy.mockResolvedValue({ data: [] } as never);
|
|
618
|
+
mockedMetricService.aggregateBy.mockResolvedValue({ data: [] } as never);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
function mockSpanSpend(value: number): void {
|
|
622
|
+
mockedSpanService.aggregateBy.mockResolvedValue({
|
|
623
|
+
data: [{ timestamp: NOW, value: value }],
|
|
624
|
+
} as never);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function mockMetricSpend(value: number): void {
|
|
628
|
+
mockUsdMetricRows([{ timestamp: NOW, value: value }]);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
test("uses spans when the span stream reported spend", async () => {
|
|
632
|
+
mockSpanSpend(42);
|
|
633
|
+
mockMetricSpend(999);
|
|
634
|
+
|
|
635
|
+
await expect(
|
|
636
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
637
|
+
budget: makeBudget(),
|
|
638
|
+
dayStart: DAY_START,
|
|
639
|
+
now: NOW,
|
|
640
|
+
}),
|
|
641
|
+
).resolves.toEqual({ spendInUSD: 42, source: "spans" });
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
/*
|
|
645
|
+
* The double-counting guard, and the reason this is a fallback rather than a
|
|
646
|
+
* sum: an SDK emitting both signals would otherwise report 1041 here, and a
|
|
647
|
+
* budget that fires on spend that never happened is worse than one that
|
|
648
|
+
* misses.
|
|
649
|
+
*/
|
|
650
|
+
test("never adds metric spend on top of span spend", async () => {
|
|
651
|
+
mockSpanSpend(42);
|
|
652
|
+
mockMetricSpend(999);
|
|
653
|
+
|
|
654
|
+
const resolved: { spendInUSD: number } =
|
|
655
|
+
await LlmCostBudgetEvaluator.resolveSpend({
|
|
656
|
+
budget: makeBudget(),
|
|
657
|
+
dayStart: DAY_START,
|
|
658
|
+
now: NOW,
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
expect(resolved.spendInUSD).toBe(42);
|
|
662
|
+
expect(resolved.spendInUSD).not.toBe(1041);
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
test("does not query metrics at all when spans answered", async () => {
|
|
666
|
+
mockSpanSpend(42);
|
|
667
|
+
|
|
668
|
+
await LlmCostBudgetEvaluator.resolveSpend({
|
|
669
|
+
budget: makeBudget(),
|
|
670
|
+
dayStart: DAY_START,
|
|
671
|
+
now: NOW,
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
expect(mockedMetricService.aggregateBy).not.toHaveBeenCalled();
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
test("falls back to metrics when the span stream is empty", async () => {
|
|
678
|
+
mockMetricSpend(17.5);
|
|
679
|
+
|
|
680
|
+
await expect(
|
|
681
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
682
|
+
budget: makeBudget(),
|
|
683
|
+
dayStart: DAY_START,
|
|
684
|
+
now: NOW,
|
|
685
|
+
}),
|
|
686
|
+
).resolves.toEqual({ spendInUSD: 17.5, source: "metrics" });
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
test("reports source 'none' when both streams are empty", async () => {
|
|
690
|
+
await expect(
|
|
691
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
692
|
+
budget: makeBudget(),
|
|
693
|
+
dayStart: DAY_START,
|
|
694
|
+
now: NOW,
|
|
695
|
+
}),
|
|
696
|
+
).resolves.toEqual({ spendInUSD: 0, source: "none" });
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
test("treats a zero-valued span bucket as no span spend", async () => {
|
|
700
|
+
mockSpanSpend(0);
|
|
701
|
+
mockMetricSpend(8);
|
|
702
|
+
|
|
703
|
+
await expect(
|
|
704
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
705
|
+
budget: makeBudget(),
|
|
706
|
+
dayStart: DAY_START,
|
|
707
|
+
now: NOW,
|
|
708
|
+
}),
|
|
709
|
+
).resolves.toEqual({ spendInUSD: 8, source: "metrics" });
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
test("a zero metric total still resolves to 'none', not 'metrics'", async () => {
|
|
713
|
+
mockMetricSpend(0);
|
|
714
|
+
|
|
715
|
+
await expect(
|
|
716
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
717
|
+
budget: makeBudget(),
|
|
718
|
+
dayStart: DAY_START,
|
|
719
|
+
now: NOW,
|
|
720
|
+
}),
|
|
721
|
+
).resolves.toEqual({ spendInUSD: 0, source: "none" });
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
/*
|
|
725
|
+
* Returning 0 on a failed metric query would silently suppress a real
|
|
726
|
+
* breach — the same reasoning behind the span aggregate's
|
|
727
|
+
* timeoutOverflowMode: 'throw'. evaluateAllBudgets isolates the failure.
|
|
728
|
+
*/
|
|
729
|
+
test("propagates a metric-query failure rather than reporting zero spend", async () => {
|
|
730
|
+
mockedMetricService.aggregateBy.mockRejectedValue(
|
|
731
|
+
new Error("clickhouse timeout") as never,
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
await expect(
|
|
735
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
736
|
+
budget: makeBudget(),
|
|
737
|
+
dayStart: DAY_START,
|
|
738
|
+
now: NOW,
|
|
739
|
+
}),
|
|
740
|
+
).rejects.toThrow("clickhouse timeout");
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
test("propagates a span-query failure without reaching the fallback", async () => {
|
|
744
|
+
mockedSpanService.aggregateBy.mockRejectedValue(
|
|
745
|
+
new Error("span boom") as never,
|
|
746
|
+
);
|
|
747
|
+
|
|
748
|
+
await expect(
|
|
749
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
750
|
+
budget: makeBudget(),
|
|
751
|
+
dayStart: DAY_START,
|
|
752
|
+
now: NOW,
|
|
753
|
+
}),
|
|
754
|
+
).rejects.toThrow("span boom");
|
|
755
|
+
|
|
756
|
+
expect(mockedMetricService.aggregateBy).not.toHaveBeenCalled();
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
test("sums multiple metric buckets", async () => {
|
|
760
|
+
mockUsdMetricRows([
|
|
761
|
+
{ timestamp: NOW, value: 1.5 },
|
|
762
|
+
{ timestamp: NOW, value: 2.5 },
|
|
763
|
+
]);
|
|
764
|
+
|
|
765
|
+
await expect(
|
|
766
|
+
LlmCostBudgetEvaluator.resolveSpend({
|
|
767
|
+
budget: makeBudget(),
|
|
768
|
+
dayStart: DAY_START,
|
|
769
|
+
now: NOW,
|
|
770
|
+
}),
|
|
771
|
+
).resolves.toEqual({ spendInUSD: 4, source: "metrics" });
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
test("passes the budget's scope through to the metric query", async () => {
|
|
775
|
+
const serviceId: ObjectID = ObjectID.generate();
|
|
776
|
+
mockMetricSpend(3);
|
|
777
|
+
|
|
778
|
+
await LlmCostBudgetEvaluator.resolveSpend({
|
|
779
|
+
budget: makeBudget({ serviceId: serviceId, llmSystem: "openai" }),
|
|
780
|
+
dayStart: DAY_START,
|
|
781
|
+
now: NOW,
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
const aggregateArg: { query: Record<string, unknown> } = mockedMetricService
|
|
785
|
+
.aggregateBy.mock.calls[0]![0] as {
|
|
786
|
+
query: Record<string, unknown>;
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
expect(aggregateArg.query["primaryEntityId"]).toBe(serviceId);
|
|
790
|
+
expect(aggregateArg.query["attributes"]).toEqual({
|
|
791
|
+
"gen_ai.system": "openai",
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
describe("LlmCostBudgetEvaluator.evaluateBudget — metric-sourced spend", () => {
|
|
797
|
+
beforeEach(() => {
|
|
798
|
+
jest.clearAllMocks();
|
|
799
|
+
mockedSpanService.aggregateBy.mockResolvedValue({ data: [] } as never);
|
|
800
|
+
mockedMetricService.aggregateBy.mockResolvedValue({ data: [] } as never);
|
|
801
|
+
mockedMetricService.insertJsonRows.mockResolvedValue(undefined as never);
|
|
802
|
+
mockedBudgetService.updateOneById.mockResolvedValue(undefined as never);
|
|
803
|
+
mockedTelemetryUtil.indexMetricNameServiceNameMap.mockResolvedValue(
|
|
804
|
+
undefined as never,
|
|
805
|
+
);
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
test("a metrics-only project gets its spend stamped on the budget row", async () => {
|
|
809
|
+
mockUsdMetricRows([{ timestamp: NOW, value: 60 }]);
|
|
810
|
+
|
|
811
|
+
const budget: LlmCostBudget = makeBudget({ dailyBudgetInUSD: 100 });
|
|
812
|
+
|
|
813
|
+
await LlmCostBudgetEvaluator.evaluateBudget({ budget, now: NOW });
|
|
814
|
+
|
|
815
|
+
const updateArg: { data: Record<string, unknown> } = mockedBudgetService
|
|
816
|
+
.updateOneById.mock.calls[0]![0] as {
|
|
817
|
+
data: Record<string, unknown>;
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
expect(updateArg.data["currentDaySpendInUSD"]).toBe(60);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
test("a metrics-only project publishes the same budget series as a span-backed one", async () => {
|
|
824
|
+
mockUsdMetricRows([{ timestamp: NOW, value: 80 }]);
|
|
825
|
+
|
|
826
|
+
await LlmCostBudgetEvaluator.evaluateBudget({
|
|
827
|
+
budget: makeBudget({ dailyBudgetInUSD: 100 }),
|
|
828
|
+
now: NOW,
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
const rows: Array<Record<string, unknown>> = mockedMetricService
|
|
832
|
+
.insertJsonRows.mock.calls[0]![0] as Array<Record<string, unknown>>;
|
|
833
|
+
|
|
834
|
+
expect(rows).toHaveLength(2);
|
|
835
|
+
expect(rows[0]!["name"]).toBe(LLM_BUDGET_SPEND_METRIC_NAME);
|
|
836
|
+
expect(rows[0]!["value"]).toBe(80);
|
|
837
|
+
expect(rows[1]!["name"]).toBe(LLM_BUDGET_PERCENT_METRIC_NAME);
|
|
838
|
+
expect(rows[1]!["value"]).toBe(80);
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
/*
|
|
842
|
+
* The published series must stay byte-identical to what a span-backed budget
|
|
843
|
+
* emits — existing monitors filter on oneuptime.llm.budget.id and must not
|
|
844
|
+
* have to learn about the source.
|
|
845
|
+
*/
|
|
846
|
+
test("the published series carries no source-specific attribute", async () => {
|
|
847
|
+
mockUsdMetricRows([{ timestamp: NOW, value: 5 }]);
|
|
848
|
+
|
|
849
|
+
const budget: LlmCostBudget = makeBudget();
|
|
850
|
+
|
|
851
|
+
await LlmCostBudgetEvaluator.evaluateBudget({ budget, now: NOW });
|
|
852
|
+
|
|
853
|
+
const rows: Array<Record<string, unknown>> = mockedMetricService
|
|
854
|
+
.insertJsonRows.mock.calls[0]![0] as Array<Record<string, unknown>>;
|
|
855
|
+
|
|
856
|
+
const attributes: Record<string, string> = rows[0]!["attributes"] as Record<
|
|
857
|
+
string,
|
|
858
|
+
string
|
|
859
|
+
>;
|
|
860
|
+
|
|
861
|
+
expect(Object.keys(attributes).sort()).toEqual(
|
|
862
|
+
[LLM_BUDGET_ID_ATTRIBUTE, LLM_BUDGET_NAME_ATTRIBUTE].sort(),
|
|
863
|
+
);
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
test("an idle project still stamps zero and publishes its series", async () => {
|
|
867
|
+
await LlmCostBudgetEvaluator.evaluateBudget({
|
|
868
|
+
budget: makeBudget(),
|
|
869
|
+
now: NOW,
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
const updateArg: { data: Record<string, unknown> } = mockedBudgetService
|
|
873
|
+
.updateOneById.mock.calls[0]![0] as {
|
|
874
|
+
data: Record<string, unknown>;
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
expect(updateArg.data["currentDaySpendInUSD"]).toBe(0);
|
|
878
|
+
expect(mockedMetricService.insertJsonRows).toHaveBeenCalledTimes(1);
|
|
879
|
+
});
|
|
880
|
+
});
|