From 2ab85fed5b9b2b86fdd01b40c116867d716df150 Mon Sep 17 00:00:00 2001 From: Nedaa Hussein Date: Wed, 4 Nov 2026 05:20:54 +0000 Subject: [PATCH 1/5] fix: show the HTTP status and error message of RUM spans in the trace view (#16288) Co-authored-by: Nedaa Hussein <317027490+Nedaa-Hussein-Apex@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) Co-authored-by: Hengfei Yang --- .../composables/rum/useRumSpanBuilder.spec.ts | 27 +++++++++++++++++++ web/src/composables/rum/useRumSpanBuilder.ts | 7 +++++ 2 files changed, 34 insertions(+) diff --git a/web/src/composables/rum/useRumSpanBuilder.spec.ts b/web/src/composables/rum/useRumSpanBuilder.spec.ts index 0076b1c28e..29f0f2ab79 100644 --- a/web/src/composables/rum/useRumSpanBuilder.spec.ts +++ b/web/src/composables/rum/useRumSpanBuilder.spec.ts @@ -1240,6 +1240,21 @@ describe("useRumSpanBuilder", () => { expect(resSpan!.span_status).toBe("OK"); }); + it("should carry the HTTP status for the span details' Error tab", () => { + const traced = makeTracedResource(); + const resource = makeOwnRequest({ + resource_status_code: 401, + resource_type: "fetch", + }); + const { formatRumEventsAsSpans } = buildComposable(); + + const spans = formatRumEventsAsSpans([traced], [], [], [resource]); + + const resSpan = spans.find((s) => s.rum_event_type === "resource"); + expect(resSpan!.span_status).toBe("ERROR"); + expect(resSpan!.http_response_status_code).toBe(401); + }); + it("should fall back to 'Frontend' when service field is absent", () => { const traced = makeTracedResource(); const resource = makeOwnRequest({ @@ -1398,6 +1413,18 @@ describe("useRumSpanBuilder", () => { expect(errSpan!.operation_name).toBe("Error: TypeError"); }); + it("should carry the error type and message for the span details' Error tab", () => { + const traced = makeTracedResource(); + const error = makeErrorEvent(); + const { formatRumEventsAsSpans } = buildComposable(); + + const spans = formatRumEventsAsSpans([traced], [], [], [error]); + + const errSpan = spans.find((s) => s.rum_event_type === "error"); + expect(errSpan!.error_type).toBe("TypeError"); + expect(errSpan!.status_message).toBe("TypeError: Cannot read property"); + }); + it("should show first 3 errors individually", () => { const traced = makeTracedResource(); const errors = [1, 2, 3].map((i) => diff --git a/web/src/composables/rum/useRumSpanBuilder.ts b/web/src/composables/rum/useRumSpanBuilder.ts index f451694ff2..1c8ab66a9f 100644 --- a/web/src/composables/rum/useRumSpanBuilder.ts +++ b/web/src/composables/rum/useRumSpanBuilder.ts @@ -320,6 +320,13 @@ export default function useRumSpanBuilder( event.type === "error" || (event.type === "resource" && event.resource_status_code >= 400) ? "ERROR" : "OK", + // What the span details' Error tab reads: a request's HTTP status, an error's type and message. + ...(event.type === "resource" + ? { http_response_status_code: event.resource_status_code } + : {}), + ...(event.type === "error" + ? { error_type: event.error_type, status_message: event.error_message } + : {}), span_kind: event.type === "resource" ? SPAN_KIND_CLIENT : SPAN_KIND_UNSPECIFIED, rum_event_type: event.type, rum_session_id: event.session_id, From a3225b96e0202c279b5c10b89421932a36542b1c Mon Sep 17 00:00:00 2001 From: Omkar Kesarkhane <127078473+omkarK06@users.noreply.github.com> Date: Wed, 4 Nov 2026 05:23:12 +0000 Subject: [PATCH 2/5] feat(traces): create alerts, add RED charts to dashboards and ask AI from the explorer (#16284) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- scripts/translations/.translation_state.json | 596 ++++++++++++++++- src/core/src/alerts/alert.rs | 97 ++- tests/ui-testing/ci-matrix/ci_matrix.json | 1 + .../pages/tracesPages/tracesPage.js | 64 ++ .../rum-product-analytics.spec.js | 4 +- .../Traces/tracesRedActions.spec.js | 96 +++ web/src/components/CodeQueryEditor.spec.ts | 43 +- web/src/components/CodeQueryEditor.vue | 10 +- web/src/components/QueryEditor.spec.ts | 24 + web/src/components/QueryEditor.vue | 5 +- .../dashboards/PanelContainer.spec.ts | 49 ++ .../components/dashboards/PanelContainer.vue | 30 + .../SelectDashboardDropdown.spec.ts | 32 + .../dashboards/SelectDashboardDropdown.vue | 37 +- .../dashboards/SelectTabDropdown.spec.ts | 17 + .../dashboards/SelectTabDropdown.vue | 13 +- web/src/composables/shared/router.spec.ts | 6 + web/src/composables/shared/router.ts | 1 + web/src/locales/languages/ar-SA.json | 612 +++++++++++++++++- web/src/locales/languages/de-DE.json | 612 +++++++++++++++++- web/src/locales/languages/en-US.json | 53 +- web/src/locales/languages/es-ES.json | 612 +++++++++++++++++- web/src/locales/languages/fr-FR.json | 612 +++++++++++++++++- web/src/locales/languages/it-IT.json | 612 +++++++++++++++++- web/src/locales/languages/ja-JP.json | 612 +++++++++++++++++- web/src/locales/languages/ko-KR.json | 612 +++++++++++++++++- web/src/locales/languages/nl-NL.json | 612 +++++++++++++++++- web/src/locales/languages/pl-PL.json | 612 +++++++++++++++++- web/src/locales/languages/pt-PT.json | 612 +++++++++++++++++- web/src/locales/languages/ru-RU.json | 612 +++++++++++++++++- web/src/locales/languages/th-TH.json | 612 +++++++++++++++++- web/src/locales/languages/tr-TR.json | 612 +++++++++++++++++- web/src/locales/languages/vi-VN.json | 612 +++++++++++++++++- web/src/locales/languages/zh-CN.json | 612 +++++++++++++++++- web/src/locales/languages/zh-TW.json | 612 +++++++++++++++++- .../plugins/metrics/AddToDashboard.spec.ts | 251 ++++++- web/src/plugins/metrics/AddToDashboard.vue | 92 ++- web/src/plugins/traces/Index.vue | 28 +- web/src/plugins/traces/IndexList.vue | 22 +- web/src/plugins/traces/SearchBar.spec.ts | 193 +++++- web/src/plugins/traces/SearchBar.vue | 177 ++++- web/src/plugins/traces/TraceDetails.spec.ts | 89 +++ web/src/plugins/traces/TraceDetails.vue | 56 +- .../traces/TraceDetailsSidebar.spec.ts | 75 +++ .../plugins/traces/TraceDetailsSidebar.vue | 49 +- .../composables/useTracesAlertAction.spec.ts | 237 +++++++ .../composables/useTracesAlertAction.ts | 144 +++++ .../metrics/TracesLatencyHeatmap.spec.ts | 38 +- .../traces/metrics/TracesLatencyHeatmap.vue | 30 + .../metrics/TracesMetricsDashboard.spec.ts | 80 ++- .../traces/metrics/TracesMetricsDashboard.vue | 81 ++- .../traces/metrics/tracesRedPanels.spec.ts | 116 ++++ .../plugins/traces/metrics/tracesRedPanels.ts | 152 +++++ .../traces/utils/nlSqlToTracesFilter.spec.ts | 110 ++++ .../traces/utils/nlSqlToTracesFilter.ts | 72 +++ .../traces/utils/traceAiPayload.spec.ts | 228 +++++++ .../plugins/traces/utils/traceAiPayload.ts | 175 +++++ web/src/utils/alerts/alertSourceRegistry.ts | 9 + .../utils/alerts/prefill/conformance.spec.ts | 20 + .../utils/alerts/prefill/fromTraces.spec.ts | 413 ++++++++++++ web/src/utils/alerts/prefill/fromTraces.ts | 250 +++++++ .../utils/traces/resolveTracesFilter.spec.ts | 59 ++ web/src/utils/traces/resolveTracesFilter.ts | 32 + .../Dashboards/RenderDashboardCharts.spec.ts | 66 ++ .../Dashboards/RenderDashboardCharts.vue | 13 + 65 files changed, 13805 insertions(+), 522 deletions(-) create mode 100644 tests/ui-testing/playwright-tests/Traces/tracesRedActions.spec.js create mode 100644 web/src/plugins/traces/composables/useTracesAlertAction.spec.ts create mode 100644 web/src/plugins/traces/composables/useTracesAlertAction.ts create mode 100644 web/src/plugins/traces/metrics/tracesRedPanels.spec.ts create mode 100644 web/src/plugins/traces/metrics/tracesRedPanels.ts create mode 100644 web/src/plugins/traces/utils/nlSqlToTracesFilter.spec.ts create mode 100644 web/src/plugins/traces/utils/nlSqlToTracesFilter.ts create mode 100644 web/src/plugins/traces/utils/traceAiPayload.spec.ts create mode 100644 web/src/plugins/traces/utils/traceAiPayload.ts create mode 100644 web/src/utils/alerts/prefill/fromTraces.spec.ts create mode 100644 web/src/utils/alerts/prefill/fromTraces.ts create mode 100644 web/src/utils/traces/resolveTracesFilter.spec.ts create mode 100644 web/src/utils/traces/resolveTracesFilter.ts diff --git a/scripts/translations/.translation_state.json b/scripts/translations/.translation_state.json index 852d97b1d0..9e528430ed 100644 --- a/scripts/translations/.translation_state.json +++ b/scripts/translations/.translation_state.json @@ -1184,6 +1184,8 @@ "select_only_two": "5085a33d498742f4", "select_two": "7761b6516e1ec2c8" }, + "costAtLeast": "817fa98cd15714a5", + "costUnknown": "d05144e2f67105ef", "create": "4759498ac2a719c6", "createError": "64f9e995370afc25", "createSuccess": "ab1d1b5741d5c2df", @@ -1195,6 +1197,7 @@ "by": "ae2134ba7e6456d5", "columns": { "dispersion": "752831e1cce1dc39", + "error": "54a0e8c17ebb21a1", "input": "36ecb4f8669133ce", "latency": "e0e7d293043c8cf6", "output": "b2439bcb8dee14b6", @@ -1216,6 +1219,24 @@ }, "costBreakdown": "49e18434853d9944", "datasetVersion": "7126d5d7ea42c517", + "failureBanner": { + "cause": "b72c30d5e0d85795", + "moreCauses": "9c946e2f07c43859", + "noMessage": "4fd9ba9822fae31a", + "openProviders": "3cb5a8c0534b6274", + "title": "87644ee8185a3f26" + }, + "failureKinds": { + "execution_error": "c1a629c0151dd33f", + "mapping_error": "9d3297a7ff61cca1", + "parse_error": "329c253b42598245", + "provider_5xx": "a87bbaf989452598", + "provider_auth": "40b272121c9a17c9", + "rate_limited": "1e11b846d9a91ce1", + "scorer_missing": "95cfae6d15eca2e8", + "timeout": "e35155bc1aa15123", + "unknown": "c1a629c0151dd33f" + }, "highDispersion": "0beb185ae4bd7009", "highDispersionBadge": "c4ebc6d4a5832cd9", "highDispersionHint": "82b84276c5c1c946", @@ -1280,6 +1301,7 @@ "descriptionPlaceholder": "dc1e7db58b2d8e76", "expectedOutputScorersOnly": "a758ba9ab3e54c96", "identitySection": "999f23fcd7bec707", + "judgeProviderFailed": "4d7a3a8c23b33790", "managedPromptLabel": "39f780053bf3874c", "managedPromptPlaceholder": "1e841b12d6e50a5c", "modelHelp": "0017404e73bb5f67", @@ -1338,6 +1360,7 @@ "tabTask": "4bc74b21357c6cf5", "taskConcurrencyLabel": "f5d40eea8e3ff761", "taskOverrideHelp": "3ff10923500ee249", + "taskProviderFailed": "42a3f56235268fe2", "taskTimeoutLabel": "1f966032d11151c8", "taskTypeInline": "2abdd5ee9b043082", "taskTypeInlineHelp": "de18d05478a65bd9", @@ -2348,7 +2371,11 @@ }, "scope": { "agent": "11b39c93777e8f1f", + "agentIdHint": "577c9d7ca5c4f39d", "env": "494d9aa0c0684432", + "noAgentIdentity": "4045f5a7d8e05d63", + "noAgentIdentityAction": "153a2dd7ec485532", + "noAgentIdentityDescription": "f3f70a28febe536c", "version": "dd167905de0defca" }, "sections": { @@ -5084,6 +5111,7 @@ "subtitle": "5fcb81394285a5a9", "systemUser": "6725e7bbcd28f3a8", "timeline": { + "ackEndedByPageResolve": "36cb1acf13c91edb", "aiFinished": "2ba863b687e16de2", "aiStarted": "707d9a19d37cdeea", "alertTriggered": "9eb862ab087e4afa", @@ -5093,6 +5121,7 @@ "analysisNeedsInputNoNeed": "d86e2fe3d7e84cf7", "assignedTo": "d6569993158f9c9c", "assignmentRemoved": "65635b6dfe0a289f", + "badgeAckEnded": "e5e6936ea3700ef6", "badgeAcknowledged": "d87cdf8aa30447b1", "badgeAiAnalysis": "c8438a13b9dd6620", "badgeAiCancelled": "1fb4d48b5386d8cd", @@ -5415,6 +5444,13 @@ "rumfunnel": { "label": "4c75600fe5d78bc8", "toast": "64d93a2540ca331b" + }, + "traces": { + "errorCount": "a6c450f7752c3b1f", + "errorRate": "33a224722fc7b1e4", + "label": "a7aa98743fde8426", + "noStream": "89cf2ea082e012d3", + "toast": "57572c39a306fa6d" } }, "warnings": { @@ -5447,6 +5483,20 @@ "textFilterDropped": "1af3560e95f9e483", "thresholdColumnGuessed": "4595598009f195f5", "thresholdNotCarried": "386d5f1dc39548e0", + "tracesErrorRatioMinSpans": "2b5eb9d30ce5f182", + "tracesErrorRatioMinTraces": "0a48e91a68610396", + "tracesNoMatches": "a3a883fe8d51faaa", + "tracesNotRun": "153a5867ff5b6841", + "tracesPeriodCapped": "d85febb31cf3f13d", + "tracesSeedDuration": "86a5310dd9829c69", + "tracesSeedErrorRatio": "6482c9b0e280236f", + "tracesSeedErrorSpans": "b7223652d982387b", + "tracesSeedErrorTraces": "6b6a2da0f49cbf2a", + "tracesSeedMatches": "6d5f6bf70a261bfe", + "tracesSeedSpans": "b0c7da48cb7e6be0", + "tracesSeedTraces": "65efc9318192f33e", + "tracesSeedUnavailable": "0eeb0bb9b1a96b48", + "tracesStoredSpans": "76b5019e760436d4", "unresolvedQuery": "4cd999a9b4222ad7", "unsupportedPanelType": "1f9928c4b86b85bb" } @@ -9355,6 +9405,15 @@ "queryFields": "bedff62661a6f43b", "titleRequired": "7c15e7448e6f4d1c" }, + "date": "99c40ab405926cb5", + "dateFormat": "16b1ff0fd29e0b60", + "dateFormatDmy": "0c6ff1231a8aa208", + "dateFormatEpoch": "53660b5bc2209228", + "dateFormatEpochms": "5389bb9f6f36e8ac", + "dateFormatEpochus": "9044784c02e163ff", + "dateFormatHelp": "9a3857e3a591b7c7", + "dateFormatIso": "bd203cdfbc050d8f", + "dateFormatMdy": "497319b9a2f9bdb6", "days": "e08c0aa8f558f39f", "decimals": "74672912045e226a", "decimalsMustBeBetween": "7fea01108f16d056", @@ -9960,6 +10019,8 @@ "addAnnotations": "cef84932bd0f98d2", "dependentAdhocVariableWarning": "a49d6008392505a5", "exitAnnotationsMode": "f7926a15fd7336d3", + "exploreAddToDashboard": "81c5e9c7bb53cc85", + "exploreCreateAlert": "a7aa98743fde8426", "pleaseWait": "2dfd888fdd5e8018", "redirectingToLogs": "11687f03a8f7d474", "refreshCacheReload": "2ac4f8a9601ba6c7", @@ -11387,6 +11448,7 @@ "lineCountedToBehind": "fda756c37a0ee644", "lineExact": "22fc00c3268448a7", "lineExactEstimated": "3abdcfd5183d925a", + "lineExactSlowestSchema": "ef3365493b226924", "lineGap": "71db3c726692dd2f", "lineHealthy": "ad6f89d68992ae3f", "lineHealthyAll": "9724ad1e593159ca", @@ -11439,6 +11501,7 @@ "failedChip": "cd8f5051cba3686a", "moreServices": "ecadd87ff0e5d0aa", "openQueries": "1cc7003257a55ac4", + "schemaCount": "af8b06d8319b5766", "searchPlaceholder": "5b5e4ae9bc9f4827", "summary": { "calls": "b73a5e2ca6458665", @@ -12597,9 +12660,14 @@ "title": "275814da8d3d0fc0" }, "noLLMInsights": { - "action": "bbefb1e0d359b886", - "actionDesc": "10143ef7e46421e8", + "action": "077f797f300db0bd", + "actionDesc": "500ad6f2550cb2fe", + "codingAgent": "a0187dc59f83a81b", + "codingAgentDesc": "a9f892db0b444d97", "description": "19ec2da72c6b2477", + "startFrom": "5f3285e4fbdb83f5", + "suggested": "7b89ba0bd02cea17", + "suggestedDesc": "450da62ec7cef9f1", "title": "c390a136b67c5c2a" }, "noLLMSessions": { @@ -12995,7 +13063,6 @@ "addCollector": "6d30a69cb01ed55a", "audit": { "allObjects": "77b8112ecb4eb493", - "assignedFleetConfig": "f2edb905b6ae3905", "autoRollback": "759dfb52f2d180e8", "createdConfig": "4f7f1ee2671cb679", "createdFleet": "b68cda33862cfd6c", @@ -13039,7 +13106,8 @@ "progressive": "c21bea0c6d590524", "rolledBack": "71592236d852c8a3", "scheduled": "4724f344c1c0e4a1", - "superseded": "e5830d265452f1ed" + "superseded": "e5830d265452f1ed", + "waiting": "6e293a8c009e0011" }, "collectorMetrics": { "chartFailures": "216322e91817a15f", @@ -13401,6 +13469,7 @@ "receiverRefused": "edcf8cddc0293bee", "rollingOut": "1034b2f3c8519a6e", "rolloutHalted": "8a2afc80498adcd1", + "rolloutWaiting": "4eb9605c57a8efaf", "throughput": "286c8abb332b94d1", "viewCollectors": "09362e686190000d" }, @@ -13468,7 +13537,7 @@ "confirmRollbackNoStable": "76cba5181ffc44a2", "confirmSkip": "fbf2627372d3e0cc", "confirmed": "fe00b67b6dd1143f", - "emptyFleet": "c83174a9b6a438d5", + "emptyFleet": "40e87c8e8e200c76", "eventConverged": "496a7f78a878cf75", "eventDeployingAll": "bc466962bfa87a7e", "eventDeployingPhase": "48b3a2550d7da3be", @@ -13483,6 +13552,7 @@ "eventStarted": "ecbc89cd37a03734", "eventSuperseded": "52ef7e19237d3ce6", "eventTimedOut": "c47d17f78f06c3c5", + "eventWaiting": "e4264c0e69310006", "excluded": "bef1fd9e5caea8b5", "exitDone": "e1094f3cf7f677e3", "exitFixForward": "470104b45177a3ae", @@ -13507,11 +13577,13 @@ "noneTargeted": "ff39f3ad54f4fc2d", "notFound": "860e2a125f52a2c9", "notFoundDescription": "86178a756a81856d", + "notStarted": "ba35f0c47d862763", "oneBatch": "c19b328d9c33d82c", "outcomeCompleted": "ecbca3631908722a", - "outcomeNoneTargeted": "46fc62713b771684", + "outcomeNoneTargeted": "962415e5e360c27e", "outcomeRolledBack": "b87a2b40dc59b1f9", "outcomeSuperseded": "2b19b3756928b29e", + "outcomeWaiting": "33b4615fa201ed60", "phase": "46342ec1eec9d78d", "phaseConverging": "5d83a1cdbc3ece78", "phaseDeploying": "4338bdb0808bff1b", @@ -13528,8 +13600,9 @@ "pickConfig": "90ea303d63b6e1b9", "pickFleet": "198dc9fab289dce0", "pickVersion": "63615c1367bcaffc", - "planEmpty": "e2d85223a4128d4a", + "planEmpty": "abc88f694f3d0380", "planOne": "a383f777ba3f4045", + "planStable": "5ce689ffe91deeb2", "planText": "1f056a0132545164", "planUnavailable": "59be1a0417cc69d6", "progress": "4664827f8e890192", @@ -13561,7 +13634,8 @@ "version": "dd167905de0defca", "versionChange": "d3f8f2614d634511", "versionFailedBefore": "9239d93df9ca801f", - "versionTo": "9dfbfacb677a4b76" + "versionTo": "9dfbfacb677a4b76", + "waitingForCollector": "6ade5abc8ce0ffc9" }, "settings": { "action": "64cff1319d2fd2cb", @@ -15155,6 +15229,18 @@ "adminRegion": "0ffda4cfc0650beb", "aerospike": "ce7999e71cdee64f", "ai": { + "codingAgent": { + "envButton": "8594d2ee749572c4", + "envHelp": "a674c8b69df5e79c", + "envTitle": "08d3da57e11a658c", + "promptCopied": "c55394776b1492cf", + "promptLabel": "5c39123805ffb4e2", + "promptTitle": "c36d206528dfdcd8", + "streamHelp": "83f9edcc4f1358f3", + "streamLabel": "cdb84491f9d46fdd", + "tabAgent": "790f1f1068bb1941", + "tabSteps": "e8d00a9b9ef410c6" + }, "selectIntegrationPrompt": "a0ce42f3b966249c", "viewDocsLinkLabel": "fcec91509759ad99", "viewDocsPrefix": "95ba4ed9329f4a25", @@ -16350,6 +16436,78 @@ "tabLabel": "4fc52a3c4c558b51", "title": "2a932f90172e99ea" }, + "llmDataTrust": { + "chart": { + "addPrice": "a569afba541a4546", + "costMissing": "15719aeadc9a4433", + "costMissingPricedSince": "ccbcc443db252b66", + "pricedSinceModels": "4b41efe0a1b586bf", + "unpricedModels": "9d02ff6116a21199" + }, + "cost": { + "addPrice": "a569afba541a4546", + "atLeast": "817fa98cd15714a5", + "partial": "9834a14ab9bcaa0f", + "partialTooltip": "7eb940bd52a38991", + "scopeSession": "3f3af1ecebbd1410", + "scopeTrace": "eafe895eb8119e6e", + "unknown": "d05144e2f67105ef", + "unknownModel": "b38aca5bdef50e36", + "unknownTooltip": "37d532fec09f7454" + }, + "health": { + "checkedAgo": "a969a64149949bb1", + "checks": { + "missing_agent_identity": { + "detail": "56be6f448f66fc25", + "fix": "153a2dd7ec485532", + "label": "534909c4fe3161d5", + "pass": "f7ac036ebd3d13c4" + }, + "missing_session_id": { + "detail": "71a7176e6a2d8e37", + "fix": "02a0a9e2c23ddca5", + "label": "7a2ac980813d4490", + "pass": "0e6992464dd45463" + }, + "unpriced_models": { + "detail": "720a8d0a56bda95a", + "fix": "a458f3394499824e", + "label": "09fe601e257b445a", + "pass": "6c7db283854bf953", + "truncated": "6803449513cad9e7" + }, + "zero_duration_spans": { + "detail": "fc8b55118ef803ef", + "fix": "a039dd154df91827", + "label": "08016c2977c1d921", + "pass": "e5fd7080ffeafe48" + } + }, + "close": "7d9eb7acb13e2462", + "copyLink": "4d3369b8d181aa30", + "drawerSubtitle": "a540f007210cda31", + "drawerTitle": "f918ce9614900771", + "linkCopied": "d12860c21e78baa5", + "loadError": "112e1f96291d4d42", + "passing": "e32467f0609830f4", + "rerun": "9df526d3b6303b71", + "samples": "9d7b125807dd8026", + "status": { + "error": "54a0e8c17ebb21a1", + "pass": "83e6fbaec303ef43", + "warning": "e981ddae45d8f4ca" + }, + "title": "6e5625b8c13013a0" + }, + "noModelName": "6e2cf4b2b853869d", + "tile": { + "pricedSinceTooltip": "057c2112c955e970", + "unpricedTokens": "12d5d476475b2caa", + "unpricedTooltip": "20b54d2be1ea235f" + }, + "topModels": "49c7abc42e15a381" + }, "llmProviders": { "columns": { "defaultModel": "8b4e8774ddf69ec7", @@ -16944,6 +17102,7 @@ "errorAddingPanel": "6f1badcb000b2865", "panelAdded": "5f65a956a6f6ad9c", "panelsAdded": "95b99ef8ae8d477d", + "panelsToAdd": "d4b7e809147d7eca", "selectDashboard": "f82c269f36cec57f", "selectTab": "096913133b1c0d72" }, @@ -17203,6 +17362,8 @@ "noMatch": "8fd65271addeef96", "noMatchHint": "f7270f66f0f6e1ad", "noMatchHintFavorites": "7d314816dbd9af58", + "noMatchYet": "4f985b77f0ddbf74", + "noMatchYetHint": "a58d01523b497cbc", "noMetrics": "9c1aac7f7110a396", "queryFailed": "222d8657e6ce5036", "railsAriaLabel": "e1fceb2e0a1984cf", @@ -17407,6 +17568,11 @@ "attributeRules": "e4bd97e927d96924", "attributeRulesDesc": "cf5a248e89b24c2b", "builtInPricesSynced": "eb9885c889598521", + "builtInStatus": { + "lastSynced": "66b7f514840b3ffc", + "neverSynced": "4d28bcc9c238aeac", + "seed": "0d9fee9d2de7a134" + }, "cancel": "19766ed6ccb2f4a3", "capturedModelsEmpty": "b97acb252c89e6fa", "capturedModelsMore": "ecccea94c62457a7", @@ -17573,6 +17739,45 @@ "tooManyRules": "1560079ec4011946", "tooManyTiers": "8c5f8e5ab314ee15", "troubleshootingTitle": "c59293ca3a3db64c", + "unpriced": { + "approximate": "78b9b22ebb6f5156", + "banner": "f5a8b26344e5eba3", + "bannerTruncated": "04c63fac69c125a8", + "colActions": "ff8059dc6752afdd", + "colCost": "7dee9df582ec20aa", + "colModel": "748fb9ab8099b3d6", + "colPricedAs": "181e4ae9e35a2002", + "colTokens": "a039dfb9628b53dd", + "createPrice": "5834e581200ba482", + "defaultTierPrices": "fc52b31ebbf086cc", + "emptyTitle": "5d5ae6accdf9641a", + "errLoad": "368bb48f479cb76c", + "errSave": "6d5ce40b8951af75", + "historicalGap": "0a1d1b0597440254", + "instrumentationDocs": "6f50e36585535ffc", + "keepPrices": "ae8f1c1c6ac489da", + "keptNotif": "cb1793b3a662a59b", + "moreAgents": "ecccea94c62457a7", + "mute": "8dd6857baf026850", + "muteConfirmMessage": "84367224ba1d13ae", + "muteConfirmTitle": "fa1be2ff9025507c", + "mutedNotif": "b75f8f814bcec848", + "noActionNeeded": "6a6527dda190a006", + "noMatch": "cd8d844d2c3dedc1", + "noModelHint": "da5b59a35e5d5ed9", + "noModelName": "6e2cf4b2b853869d", + "noStreamsDesc": "8cf4bf6441c1549a", + "noStreamsTitle": "e4c51dcda2093282", + "notPriced": "5805ffba6946917c", + "pricedNow": "763cb445aed37244", + "recordedCostNote": "76837cb622d7118d", + "spans": "7c048203171e0876", + "streamLabel": "1eec3071dd26e224", + "subtitle": "08f38ded20b5a9a2", + "title": "2a0a598ff51711b5", + "window": "5c37cf8f018b4ac5", + "windowRange": "603aaefffcd2060b" + }, "urlImportTab": "c4402d38a92d1c09", "usageKeyCol": "5e046807c81ec9dc", "usageKeyContainsSpaces": "eafd75e5ea847a68", @@ -17960,6 +18165,7 @@ "escalationEditing": "27e39fff96e0b4e8", "escalationHeld": "6eb2dc1844144383", "escalationLoading": "d441577128d7f2a1", + "escalationNobodyPaged": "cd1fc03ae9d071e5", "escalationNotStarted": "e626cc87c1c4a26c", "escalationPagesNobody": "760137c721470566", "escalationPagingStep": "1a1a433a79fa9dc9", @@ -18509,6 +18715,7 @@ "removeMemberFailed": "af28d51aed3a83d8", "removeMemberHandsOver": "4a9c9cda70d29445", "removeMemberLeavesGap": "656a7ac9de0c7886", + "removeMemberReorders": "8744a543b4facebb", "removeMemberTitleNamed": "13a85193691c916c", "removeMemberTitlePerson": "01c1f0250884f593", "removeRotationBlocked": "bbc3c91dfe484667", @@ -18613,13 +18820,14 @@ "rotationOpenMembers": "6e5e1e137765681a", "rotationOrderHint": "fe09e9fc139c1309", "rotationPreviewEmpty": "e9da2a6f8981aa03", + "rotationPreviewNow": "00174097054e8787", "rotationPreviewPairLegend": "7f1739c73bac3a77", "rotationRestrictionAdd": "b7283e2a89b85b34", "rotationRestrictionDays": "e08c0aa8f558f39f", "rotationRestrictionFrom": "218197693424e015", "rotationRestrictionRemove": "c3812fc4acb861d5", "rotationRestrictionTo": "f4b06ef6d3c81436", - "rotationRestrictionZeroLength": "62df11bf4852e883", + "rotationRestrictionZeroLength": "f9b1b36b936f9a5a", "rotationRetiredTag": "a9f71bc2404c9193", "rotationRuleCount": "ff07023b114bde98", "rotationSectionRules": "9cb43d33ba6744d4", @@ -18704,7 +18912,7 @@ "ruleEditorConflictKeptBy": "74be5c72b7d753aa", "ruleEditorConflictSameTeam": "884d0e10ac781669", "ruleEditorConflictTakesOver": "16ac75b9b76a18fc", - "ruleEditorConflictUnclaimed": "27d5e24d428ffc85", + "ruleEditorConflictUnclaimed": "bff6bd6c7ad32bd3", "ruleEditorDiscardCondition": "eb1a70e39274bb76", "ruleEditorEmpty": "b4099d03179c1240", "ruleEditorFieldPlaceholder": "e867f9b7c5ea6bf9", @@ -18799,6 +19007,7 @@ "scopePathEmpty": "db4fd2a829db4309", "scopeServiceHelp": "177357ac04c01152", "scopeValuePlaceholder": "0b24aed53ab654ea", + "scopeValuesSeen": "d9b779ee40d035ed", "searchResponses": "9b56eff16ec33c42", "searchTeams": "3ad7e221faf10419", "searchTeamsPeople": "84bc7b36a4e6ff5f", @@ -18808,6 +19017,7 @@ "sectionHint_resolved": "7ccc927dd026552c", "sectionHint_ringing": "267e1c559487547e", "sectionHint_ringingRouted": "eed6237eeccd471c", + "sectionHint_ringingStopped": "8ca9ade8957410db", "sectionHint_ringingUnrouted": "5fd68e3f69e31c51", "sectionHint_snoozed": "632057805efe13d1", "section_handled": "1e7536d56c45d9cd", @@ -19107,6 +19317,10 @@ "unroutedSignal": "1e9806e4227ba3b9", "unroutedTitle": "c0f5487da1e37214", "unroutedWriteRule": "75f7e48ed0927929", + "unsnooze": "94798842ac921e31", + "unsnoozeFailed": "a9e896a723df69d0", + "unsnoozeHint": "0f39fce573e74e53", + "unsnoozed": "ba964e0ca1d094b3", "upcoming": "cd41bc1383278f4a", "upcomingFrom": "51449acb1f33eb7e", "upcomingNoChange": "c3e7c9ebaf11435d", @@ -19145,6 +19359,10 @@ "deleteError": "4e1252dd62c17a4b", "deleteTitle": "0f81c0d86606f518", "deleted": "053cce198e4c5f50", + "disabled": { + "description": "81eee881644382fd", + "title": "91aea42163f5c2c6" + }, "failedToDeriveSchema": "7c0920d61024a096", "failedToFetchCatalog": "3d180cc1e30ede2e", "failedToImportScoreConfig": "cea17a4a8ef74d36", @@ -19172,7 +19390,7 @@ "importTypeRequired": "69bc45f13263fda6", "invalidJsonFormat": "70410685471d0577", "job": { - "backTo": "c27068e179c5a5a9", + "backTo": "b9f4405cc53cea2c", "columns": { "actions": "ff8059dc6752afdd", "created": "d70b9e24bca26b40", @@ -19291,6 +19509,7 @@ "checked": "d5963b914e843b1f", "degraded": "a8494c12f2243903", "healthy": "7f1e323b6272c620", + "lastScored": "0409ebe70cb2950b", "noRecentRuns": "060b3875c66ccf92", "since": "8dbe1c0bde15c300", "summary": "d308c48030d712ba", @@ -19519,7 +19738,7 @@ "authEditNote": "e8fff442aa27b600", "authSection": "66880d2d8216260d", "availableModelsLabel": "9dbfd0478bd51235", - "backTo": "9f73d5ebe9024c6b", + "backTo": "996c32b35f2182a9", "cannotRename": "1219ec44a131d522", "createTitle": "917817050bd786f8", "defaultModelLabel": "3840d9d29421c46c", @@ -19800,7 +20019,7 @@ "authConfigured": "20158224750041d6", "authLabel": "e3b389c6838e46ca", "authSection": "66880d2d8216260d", - "backTo": "ec7bdbca67479176", + "backTo": "d8598427499abb65", "badgeLlm": "c7b08a64bb5189e8", "badgeRemote": "ffa98e02f7ce33fc", "columns": { @@ -19991,6 +20210,9 @@ "saveError": "20354bc384081c3f", "searchPlaceholder": "b73c243999e70f74", "selectedPrefix": "bef9d85df8ea13fe", + "temperatureHelp": "159b6f26f887d3e5", + "temperatureLabel": "b958ce8b871ab36b", + "temperaturePlaceholder": "5feceb66ffc86f38", "testPanel": { "disabledHint": "af9dfbb4c32b55f3", "emptyPrefix": "5244a9d3af4c95c3", @@ -20023,6 +20245,7 @@ "nameRequired": "0e277315963f1277", "providerRequired": "caa0c95d885aab0c", "retriesInvalid": "12b3bb4259e8ce7d", + "temperatureInvalid": "1ad25a87a7426b05", "timeoutInvalid": "1a85eee09fd39cf2" }, "versionsLoadError": "211bf839fb3e9353" @@ -20332,6 +20555,13 @@ "constant": "8ab253e4aab66e2a", "csv": "90afcf937330e97f", "custom": "494ca78f7374e46f", + "date": "99c40ab405926cb5", + "dateDmy": "9c7d6e0f947c2cdc", + "dateEpoch": "595572802984f2ff", + "dateEpochms": "65720c8b01f4753f", + "dateEpochus": "0caeb49eaed902cf", + "dateIso": "3b3dd31839bb4468", + "dateMdy": "ec48d7101d123086", "doublequote": "0e1af56126edd381", "list": "e547bf4f1b91ebb8", "pipe": "b2114e05a8ef3080", @@ -24623,6 +24853,7 @@ "outboundEmpty": "322ad255e684c217", "outboundQueue": "164d6d484caf393a", "refreshTokenKept": "bb4cab940d1dea50", + "refusedPosts": "95b8e76abaf0d6bb", "reinstall": "c630a782a9eb8569", "reinstalled": "cffd0c1d3780790e", "sockets": "9ed979d196a60475", @@ -27821,6 +28052,7 @@ }, "workflows": { "chooseATriggerNodeToStart": "7904798bda88e090", + "endpointUrlCopied": "2f9b25cbd02e98a8", "onlyOneIncomingConnectionToA": "b2624193ef7b83f2", "thisBranchAlreadyEndsInA": "2c04309c6063252a", "thisConnectionWouldCreateALoop": "1bfe0227517d5d1d" @@ -27836,6 +28068,13 @@ "applyAndSearchEquals": "0957a5c642435266", "applyAndSearchNotEquals": "f27e9d0b738f3717", "applyPendingFilters": "ce1202e0d05abe3b", + "askAi": { + "askAboutSpan": "0c4630fa1207852b", + "explainTrace": "e70067d9b76c657b", + "explainTraceHint": "0ddeb2fc8e242088", + "spanChip": "1e19b7d756495f48", + "traceChip": "f52267dc28d9470a" + }, "backToLogs": "ed627b7ddd4e0cca", "backToResults": "c7ef0e278b1e08c0", "backToTraces": "f03da00bbeb8113f", @@ -28038,6 +28277,7 @@ "tool": "783d36e81261aee8" }, "lLMErrorTable": { + "loadFailed": "938bec8eff1825c7", "noData": "3b41ba9c7cb8c5d6", "noErrorsInRange": "fbbc2eb15f5981f1", "operation": "0f044feb6ca7d676", @@ -28103,6 +28343,7 @@ } }, "nextMatch": "825e5abd0762be6e", + "nlNeedsSql": "effe40b58861fd76", "noCorrelatedLogsFound": "643231c56fd2784d", "noData": { "askAi": "0dd9b2668db5d2c3", @@ -28147,6 +28388,23 @@ "operation": "0f044feb6ca7d676", "p50Median": "76661f0c8ac6ce54", "previousMatch": "daa2f8c3fc897cc7", + "redActions": { + "addRedToDashboard": "f9995c951ada8920", + "bucketNotice": { + "days": "4dbd138c5b05a855", + "hours": "7e91f22901a70179", + "minutes": "d429f1479eca3f89", + "seconds": "65cac4f4920546e5" + }, + "bucketNoticeSingle": { + "days": "e941f1e462b76056", + "hours": "b9ff968eedf27ef7", + "minutes": "e3290078adb23ed8", + "seconds": "f72027031f9cacbd" + }, + "durationActions": "604c22760aaacfb3", + "menuGroupDashboards": "a53bcafb67d960df" + }, "redMetrics": "a9d1ab9a2d6d4dac", "results": { "errorColumn": "54a0e8c17ebb21a1", @@ -28374,7 +28632,7 @@ "estimatedWithoutCache": "bcd4cd46920456b4", "filterErrors": "4eac71524e3ed530", "grossCacheReadSavings": "e39d337acfe8718d", - "latencyLead": "bff1c3d5d14f51ce", + "latencyLead": "3fb271649a2b7693", "netCacheImpact": "4bdd0ac6e0dadd9c", "resetFilters": "660f3d76f4f2802d", "tokens": "e7d163813b8a11d0", @@ -28760,6 +29018,10 @@ "folder": "74ccd43303847f26", "header": "e2073645cb0dbbdd", "history": { + "alertFiredEmptyTitle": "817e727c68262cf8", + "allTriggers": "93b7f47d48675fef", + "detailsNoKey": "07b1ae92b6fde2e4", + "detailsWithKey": "86d2daf01b268871", "duration": "4fc52a3c4c558b51", "durationHoursMinutes": "425dc6c72de6fdc5", "durationMinutesSeconds": "c4521614d0a2513f", @@ -28768,25 +29030,327 @@ "ended": "7cdc804e6996997d", "error": "54a0e8c17ebb21a1", "failed": "031a8f0f659df890", + "filterEmptyDimmed": "9b00c21e02ed1ae1", + "filterEmptyTitle": "c7a5be937e62a70f", "hideTestRuns": "39ef1b67cd8d8c06", + "incidentEventEmptyTitle": "d736cf98147a0ceb", "loadError": "762d0146abbad86f", "loadRunError": "a6300f95d9e4e4aa", "open": "0e76960093379060", "openFullView": "fd43711ac463dacb", + "openRun": "95fe7393c85ec788", + "reasonExpired": "555b5a57d6f0ab77", + "reasonRunTimeout": "86f367a202f28c1c", + "reasonWorkerLost": "00698d7c48b1fafe", + "reasonWorkflowDisabled": "8715207062f345f0", "retry": "942087cc2d41e013", "retryError": "1ff77ecb8550b8f3", "retryMessage": "abcfc90ca7b23fa9", + "retryOf": "f1dc656b1bd5c5d4", "retryStarted": "ca21bfeee92fdf35", "retryTitle": "e3399695e101743e", "retryTooltip": "ce203c4a3bbbe229", + "run": "00d60e31a4e6b834", "runType": "baaddf70fb5d432b", + "searchEmptyAction": "3b7ea51793e9d906", + "searchEmptyTitle": "813c63791d953182", + "searchRuns": "f93a3e32be86c829", "showTestRuns": "5261cabacd0dba01", + "source": "0e570ca6fabe24f9", + "sourceAlertFired": "4a2b79784a7b2c42", + "sourceDetails": "95de492834d708ef", + "sourceHttp": "f1ee1152c8b29ea2", + "sourceIncidentEvent": "a7b1ea3b90935a8c", + "sourceManual": "84a2e40670df461b", + "sourceOther": "f97e9da0e3b879f0", + "sourceRetry": "2f9c439b3b7e705c", "started": "ecbc89cd37a03734", "status": "920e413c7d411b61", + "statusExpired": "424a2551d356754c", + "statusQueued": "661ff40a07e037bb", + "statusRunning": "f4ccae29e1bb0c20", "success": "c88a0b907419a70c", "testRun": "fe9e2fc32433289d", "testRunTooltip": "a350cfc8353cff85", - "title": "74c90dadee1b9dff" + "timelineAgoDays": "02ee8a8b0b7869f7", + "timelineAgoHours": "a852514c755e2aa0", + "timelineAgoMinutes": "1695a4da57d5776d", + "timelineNow": "ed5eb9a37e2d8231", + "title": "74c90dadee1b9dff", + "triggerFilter": "8b9c643731c94119" + }, + "http": { + "acceptRequests": "3cc264203f3e1279", + "active": "92340695899bd2d8", + "advanced": "9f088dbebd6c3c70", + "advancedSummary": "9a2f2b2aa43ce830", + "authAbout": "3220ba9a4dd64473", + "authInfo": "732a42bf45eceac6", + "authLabel": "80ee37242dc6e62d", + "authSigned": "b6cc80d300c385f7", + "authToken": "d2089be672953d11", + "bodySizeBytes": "7b315b762548b35b", + "bodySizeKiB": "e6d5832839cceed2", + "bodySizeMiB": "fcebbf180a73d0ff", + "changeFailed": "912a8e5f6a6dbe10", + "colActions": "ff8059dc6752afdd", + "colCreated": "d70b9e24bca26b40", + "colLastUsed": "830ec7f812f9d862", + "colSecret": "7e32a729b1226ed1", + "colStatus": "920e413c7d411b61", + "colToken": "d2089be672953d11", + "commandCopied": "e5c02424e511e1ef", + "copyCommandFailed": "7a820eb530322b2d", + "copyCredentialChanged": "8fa5d36afff4a1c4", + "copySecret": "9b80d4b38e70c8c5", + "copyToken": "d35b4ef7c618415a", + "copyUrl": "fa26011bf19e1198", + "copyUrlDraftHint": "156f2342d5347b2b", + "copyUrlFailed": "c4858d355d69b7da", + "createForbidden": "945cad3763fad357", + "creatorGone": "a20f7dd7ec9e5c62", + "deleteBody": "34687f0a6c2d1e71", + "deleteButton": "6772d678942a3f79", + "deleteFailed": "746079e0af29c480", + "deleteMeta": "864130edac832e4e", + "deleteTitle": "77a7901069cca1d0", + "disableBody": "704424a23fa25a47", + "disableConfirm": "06f0e210b27d4d62", + "disableTitle": "51d730dcadce4a68", + "disabledBanner": "da203b3cb4b1c44d", + "done": "11a6767d5674c7e4", + "draftBanner": "de322c663fcaefee", + "dryRunAccepted": "b47d43594a5ceade", + "dryRunFailed": "6a09aecdacb8cd19", + "dryRunInput": "1e1aec27e370bdc6", + "dryRunMeta": "c68f730740081514", + "dryRunRateLimited": "9456b05c8d09541a", + "dryRunRefused": "29dff3e9aecda49c", + "dryRunRefusedPaused": "1221589dfc7065fc", + "dryRunStatus": "cb609f2cb8bd987a", + "editorButton": "9175da02d25c7b55", + "editorButtonOff": "ca7981b46ecf2c17", + "editorButtonOn": "130011756125313c", + "editorButtonRefusedDraft": "82ef1aad083c253c", + "editorButtonRefusedPaused": "bfb25e742fb6c00f", + "editorButtonTooltip": "3469d34ee7dcb761", + "endpointExistsHint": "ea4b1b3981c729a7", + "endpointExistsHintSigned": "ab98fd4d3b859b94", + "endpointUrl": "2578179d177903e1", + "errorNoLiveCredential": "87f27efdc67df9cb", + "errorNotAllowed": "7db15c5ce04c9b80", + "errorNotConfigured": "e86bbf9c69347b02", + "exampleActiveSecret": "5632f0f02fb77a6d", + "exampleActiveToken": "8e9746411032ddc5", + "exampleHintSigned": "2981e1413d06f5a0", + "exampleHintSignedPlaceholder": "2e4b4f0a0b5fd31b", + "exampleHintToken": "e03506daef6440d1", + "exampleHintTokenPlaceholder": "e6d0b3bf9749bd74", + "exampleRequest": "ba15af9e002393bc", + "expired": "424a2551d356754c", + "expiresInDays": "da3b526ec02bb68c", + "expiresInHours": "cf5e5e97f6a66453", + "fieldGroup": "f1ee1152c8b29ea2", + "fieldGroupBodyHint": "b6f6cb95ff15cdc6", + "fieldGroupQueryHint": "088f9b97cb821261", + "headerName": "dcd1d5223f73b3a9", + "headerValue": "8e37953d23daca5f", + "headers": "194e9fe656a1bcab", + "headersKept": "5b48d0229dcb6ff6", + "kindNote": "79b46f794b73b152", + "kindNotePayload": "3ae28c7412d6a4ee", + "kindNotePayloadHide": "0db5f0cba18f2d02", + "lastCall": "216689da1c52bf4f", + "lastCallNone": "97838d7724882b07", + "lastCallNoneRefused": "e0fc67bb703564fc", + "lastCallStatus": { + "expired": "fa64ea1e82e1206f", + "failed": "5d28a90f4498a814", + "queued": "d36be6494248ee06", + "running": "c071cf5f5ed6f884", + "succeeded": "5dceaeceb6d8a49d" + }, + "listMenuItem": "3cc0f1ee2da02e48", + "listTag": "56d6f32151ad8474", + "listTagTooltip": "037f01286a33aab5", + "loadError": "361318790da4cf42", + "maxBody": "198c607c2fcd6e5c", + "maxBodyHint": "5d4ded1f1ec22b8c", + "maxBodyRange": "e3549f9f86858835", + "needsAdmin": "b4d4fce3546efbdd", + "needsEdit": "a000127857e9a71f", + "never": "6300ef800bb88429", + "newSecretLabel": "c08c84dbb0cb4fc7", + "newTokenLabel": "fe46cb83ad1c5a35", + "nodeBadge": "cc5eaa11c684a30b", + "offBody": "e540546366294c8d", + "offExistingBody": "b2dcc2dacb5e1082", + "onMeta": "fed6287a4b973884", + "openStep": "463ad0ca73f8f4f4", + "outputCaption": "1e98c9dc3f8b5fbe", + "overlapDay": "f0514e8df84107f4", + "overlapHour": "f8b8883f0c9d8d82", + "overlapNow": "24a5d1cd7649d755", + "overlapWeek": "7f920bb639c93075", + "panelSubtitle": "3b1fec9ffcfc9e63", + "panelTitle": "9175da02d25c7b55", + "payloadFrom": "e64a6482125e8f6f", + "payloadHttp": "f1ee1152c8b29ea2", + "pickerCue": "0bf4a26c86ee6816", + "raiseLimit": "3bdea189a52695ed", + "rateLimit": "aa6d477fff29576b", + "rateLimitHint": "852659db035dc921", + "rateLimitRange": "748b3466f6b8cc93", + "readOnlyOnMeta": "f0ea7fe4f448fb28", + "reason": { + "credential_in_url": { + "fix": "fde4cf14cc9be644", + "title": "41805d7e323055c7" + }, + "endpoint_disabled": { + "fix": "957a2133fa4c1163", + "title": "310f36c6306d4469" + }, + "idempotency_key_reused": { + "fix": "3bfd0c548277cca9", + "title": "f6a5f27cbcba7be5" + }, + "invalid_idempotency_key": { + "fix": "96709b3d11df60ba", + "title": "a3d2a892413b9971" + }, + "invalid_signature": { + "fix": "5512fc3bcdbd7e2a", + "title": "97b096319f1c128b" + }, + "invalid_token": { + "fix": "f8e8eed7b229ca08", + "title": "086efea7640e6806" + }, + "payload_too_large": { + "fix": "0eefeb2f57a45ab2", + "title": "c3123ec25291b935" + }, + "queue_full": { + "fix": "8ee9cadfe4b71ea8", + "title": "4ad672a94c1af12d" + }, + "rate_limited": { + "fix": "559bb643b0911245", + "title": "a06130a5a2818ae5" + }, + "stale_timestamp": { + "fix": "de9286cb2791bc18", + "title": "526d8400134898e3" + }, + "worker_unavailable": { + "fix": "69cd89d5d1f8c516", + "title": "be65838a2c9df8a9" + }, + "workflow_disabled": { + "fix": "c5a3b8eee8f425df", + "title": "533631053f3864bf" + }, + "workflow_not_published": { + "fix": "0b9ae7f66b3ada69", + "title": "7f626c86b88eb6ac" + } + }, + "regionBanner": "94cbb3b56a79bc8c", + "regionTag": "9c516d4563be01e4", + "rejectionMeta": "665bb7a6e1028e75", + "rejectionsCaption": "92af1abe4eb8c381", + "rejectionsHide": "ac20a57bfde0bbc4", + "rejectionsLine": "5b35ad1e9a78b17a", + "rejectionsLoadError": "0571e6b78444c5ce", + "rejectionsShow": "0df6f1cad36c49da", + "rejectionsTitle": "3486ecce0d1ddc1a", + "request": "59f03d642b41e22f", + "requestBodySize": "895fec844e359f4c", + "requestCallerIp": "cc8f4f4a308c78bf", + "requestContentType": "6f51cb0403204b19", + "requestIdempotencyKey": "e904f4599aa01bf8", + "requestReceived": "49f19beeecf442ef", + "requestToken": "d2089be672953d11", + "retiring": "fd2b42810f6b4d83", + "revealBannerBody": "53d7d500858d0e9c", + "revealBannerBodySigned": "1d7803265dc1e4f5", + "revealBannerTitle": "a6f921fabd574f78", + "revealDenied": "b126c72b919fba7d", + "revealFailed": "6efd01b4c3d5ac96", + "revealFailedSigned": "98866207d6a91bad", + "revoke": "87e6d00bbf53ec5a", + "revokeFailed": "b7e634223402ed50", + "revokeNow": "581e15bd3fcf5a3a", + "revokeTitle": "b840925a7ecddfa5", + "rotate": "c3613b1704f54c8b", + "rotateBannerBody": "da801b4fee507396", + "rotateBannerBodyFresh": "7eb873184b6208ba", + "rotateBannerBodyFreshSigned": "ee624a55ecab5eab", + "rotateBannerBodyRevoked": "ecab40fb07e4cb33", + "rotateBannerBodyRevokedSigned": "98ba812645669ee5", + "rotateBannerBodySigned": "2aa63c54f2aa7a65", + "rotateBannerTitle": "499360406bcbd4a2", + "rotateBannerTitleSigned": "ae2310454ff61733", + "rotateBlocked": "cd6525a96b01bd5d", + "rotateBlockedSigned": "a49e5568af5fdf92", + "rotateConfirm": "4ade788283fe9a33", + "rotateConfirmSigned": "ca38c242319770f6", + "rotateConsequence": "73a9612eaae73a20", + "rotateConsequenceNow": "cf03588511c5f9ab", + "rotateConsequenceNowSigned": "25e077ce94930d8d", + "rotateConsequenceSigned": "a059892362ef0b40", + "rotateFailed": "1ed1e49a57c647f7", + "rotateLead": "b9f7594e1c806386", + "rotateLeadNone": "8a8a18bb31fa2981", + "rotateLeadNoneSigned": "bfd5eca9e538d7a9", + "rotateLeadSigned": "a32df55035f99b20", + "rotateLeadUnused": "c17f8983e70ef5ef", + "rotateLeadUnusedSigned": "3c142807e208c876", + "rotateLink": "c3613b1704f54c8b", + "rotateTitle": "4ade788283fe9a33", + "rotateTitleSigned": "ca38c242319770f6", + "runExpiredBanner": "29b5e42c19354b80", + "runFailedAt": "9fa3375cdfb43ac7", + "runGuardHint": "1dcad435ba54bae6", + "runTest": "e031e0df30c9898f", + "runTestHint": "608d160dcbb88dfb", + "runTestHintList": "b9d55d8957013535", + "runTestInstead": "c2f14a4b48e42849", + "runWorkerLostBanner": "c6bece3713bb606a", + "runsEmptyAction": "c6f6e25fc8a4902a", + "runsEmptyBody": "f2cb2f033bbf7d7e", + "runsEmptyTitle": "7cef77a71d7dd563", + "saveAndCreate": "5f5d85b59343b9a7", + "saveLimitsFailed": "17a15ea8a14e487d", + "secretCopied": "704aed35a6feded1", + "secretHint": "1ebbb4569e65cf09", + "secretLabel": "e7ee22e117a12ad8", + "secretStorageWarning": "26443323470d640b", + "secretsTitle": "37364fc41ded5401", + "sendTest": "5d2372e63ec25729", + "sendTestHint": "aaf9e310393d92b4", + "sendTestHintPaused": "192b439940d2f64d", + "sendTestSigned": "7861ceb0624b45cb", + "snippetsLabel": "ba15af9e002393bc", + "switchToSignedBody": "a0882a162191a445", + "switchToSignedTitle": "094adfc152298a2d", + "switchToTokenBody": "c35928b6d2c2105b", + "switchToTokenTitle": "1e28c9f6e0346c24", + "switchedBannerTitle": "96993ffc5037d390", + "switchedBannerTitleSigned": "5b31071a6b92c710", + "tokenCopied": "c9feac7acc32ee92", + "tokenHint": "c48da8cbbe1a2614", + "tokenLabel": "d2089be672953d11", + "tokensTitle": "a039dfb9628b53dd", + "triggerLinkOff": "5ca56d9aca5f5e51", + "triggerLinkOffAction": "bb9c27d72fa44e74", + "triggerLinkOn": "e3dd331f610bb066", + "triggerLinkOnAction": "695ff3927de6f267", + "turnOnFailed": "fcd76bcbf00c543a", + "unsavedBody": "066ff1cac3a503d8", + "useAsTestInput": "1a478a159532c84a", + "viewRun": "aaf7fccc7d1315f2" }, "linkAlerts": { "alertsLabel": "f62e9267d196c761", @@ -29008,6 +29572,8 @@ "lastTestRun": "585b1ab789dddea8", "notConfigured": "c5418aa5e0f85e5b", "notVerified": "ec3edb8410037a74", + "payloadFrom": "e64a6482125e8f6f", + "payloadFromHint": "c50945a0b865fc7c", "recordedFailure": "d4f33864645e20c9", "recordsUnavailable": "9d86d83b1749c16f", "rehearsalOnly": "fa849ce09502071d", diff --git a/src/core/src/alerts/alert.rs b/src/core/src/alerts/alert.rs index 687c1a1899..36288c5982 100644 --- a/src/core/src/alerts/alert.rs +++ b/src/core/src/alerts/alert.rs @@ -4392,16 +4392,7 @@ async fn build_notification_context( None => String::new(), } } else if alert.stream_type == StreamType::Traces { - // The traces page takes a filter expression in `query`, not SQL, so the query is left out. - format!( - "{}{}/web/traces?stream={}&from={}&to={}&org_identifier={}&tab=spans", - cfg.common.web_url, - cfg.common.base_uri, - alert.stream_name, - alert_start_time, - alert_end_time, - alert.org_id - ) + traces_alert_url(&cfg, alert, alert_start_time, alert_end_time) } else { match alert.query_condition.query_type { QueryType::SQL => { @@ -4538,6 +4529,29 @@ async fn build_notification_context( chart_png: None, } } + +fn traces_alert_url(cfg: &config::Config, alert: &Alert, start_time: i64, end_time: i64) -> String { + let mut url = format!( + "{}{}/web/traces?stream={}&from={}&to={}&org_identifier={}&tab=spans", + cfg.common.web_url, + cfg.common.base_uri, + alert.stream_name, + start_time, + end_time, + alert.org_id + ); + // The traces page takes a filter expression in `query`, not SQL, so only the WHERE is carried. + if alert.query_condition.query_type == QueryType::SQL + && let Some(sql) = &alert.query_condition.sql + { + let where_clause = config::meta::sql::extract_where(sql).where_clause; + if !where_clause.is_empty() { + url.push_str("&query="); + url.push_str(&base64::encode_url(&where_clause)); + } + } + url +} pub fn get_row_column_map(rows: &[Map]) -> HashMap> { let mut vars = HashMap::with_capacity(rows.len()); for row in rows.iter() { @@ -9592,14 +9606,13 @@ mod rule_notification_tests { assert!(matches!(error, AlertError::PromqlMissingQuery)); } - #[tokio::test] - async fn traces_alert_url_opens_the_traces_page() { + async fn notification_url_for_traces_alert(query_type: QueryType, sql: Option<&str>) -> String { let mut alert = Alert::default(); alert.org_id = "org".into(); alert.stream_type = StreamType::Traces; alert.stream_name = "default".into(); - alert.query_condition.query_type = QueryType::SQL; - alert.query_condition.sql = Some("SELECT span_id FROM \"default\"".into()); + alert.query_condition.query_type = query_type; + alert.query_condition.sql = sql.map(Into::into); let options = ProcessTemplateOptions { rows_end_time: 0, start_time: None, @@ -9611,12 +9624,15 @@ mod rule_notification_tests { resolved: false, }; let context = build_notification_context("org", &alert, &[], &[], options, None).await; - let url = if let Some((_, suffix)) = context.alert_url.split_once("/web/short/") { + if let Some((_, suffix)) = context.alert_url.split_once("/web/short/") { let id = suffix.split('?').next().unwrap(); short_url::retrieve(&alert.org_id, id).await.unwrap() } else { context.alert_url - }; + } + } + + fn assert_plain_traces_url(url: &str) { assert!(url.contains("/web/traces?stream=default&")); assert!(url.contains("&from=") && url.contains("&to=")); assert!(url.contains("&org_identifier=org&")); @@ -9625,4 +9641,53 @@ mod rule_notification_tests { assert!(!url.contains("sql_mode")); assert!(!url.contains("query=")); } + + #[tokio::test] + async fn traces_alert_url_without_where_opens_the_traces_page() { + let url = notification_url_for_traces_alert( + QueryType::SQL, + Some("SELECT span_id FROM \"default\""), + ) + .await; + assert_plain_traces_url(&url); + } + + #[tokio::test] + async fn traces_alert_url_keeps_the_where_filter() { + let url = notification_url_for_traces_alert( + QueryType::SQL, + Some("SELECT count(*) AS cnt FROM \"default\" WHERE service_name = 'api' AND duration > 100"), + ) + .await; + assert!(url.contains("/web/traces?stream=default&")); + assert!(url.contains("&org_identifier=org&")); + assert!(url.contains("&tab=spans")); + assert!(!url.contains("sql_mode")); + let query = url + .split_once("&query=") + .unwrap() + .1 + .split('&') + .next() + .unwrap(); + assert_eq!( + base64::decode_url(query).unwrap(), + "service_name = 'api' AND duration > 100" + ); + } + + #[tokio::test] + async fn traces_alert_url_skips_unparsable_or_non_sql_queries() { + assert_plain_traces_url( + ¬ification_url_for_traces_alert(QueryType::SQL, Some("not sql WHERE")).await, + ); + assert_plain_traces_url(¬ification_url_for_traces_alert(QueryType::SQL, None).await); + assert_plain_traces_url( + ¬ification_url_for_traces_alert( + QueryType::Custom, + Some("SELECT * FROM \"default\" WHERE service_name = 'api'"), + ) + .await, + ); + } } diff --git a/tests/ui-testing/ci-matrix/ci_matrix.json b/tests/ui-testing/ci-matrix/ci_matrix.json index 39c7839edc..6c8d083d70 100644 --- a/tests/ui-testing/ci-matrix/ci_matrix.json +++ b/tests/ui-testing/ci-matrix/ci_matrix.json @@ -593,6 +593,7 @@ "tracesTriageLoop.spec.js", "traceGenAiParts.spec.js", "tracesCharts.spec.js", + "tracesRedActions.spec.js", "traceSessionReplay.spec.js", "traces-jk-navigation.spec.js", "service-graph-side-panel.spec.js" diff --git a/tests/ui-testing/pages/tracesPages/tracesPage.js b/tests/ui-testing/pages/tracesPages/tracesPage.js index fbaec4e887..6b7ddff954 100644 --- a/tests/ui-testing/pages/tracesPages/tracesPage.js +++ b/tests/ui-testing/pages/tracesPages/tracesPage.js @@ -3398,4 +3398,68 @@ export class TracesPage { const cap = setTimeout(finish, maxMs); }), { minExamples, maxMs, intervalMs }); } + + // ============================================ + // CREATE ALERT / ADD TO DASHBOARD (RED actions) + // Source: web/src/plugins/traces/SearchBar.vue, PanelContainer.vue, metrics/TracesLatencyHeatmap.vue + // ============================================ + + async openMoreActionsMenu() { + await this.page.locator('[data-test="traces-search-bar-more-actions-btn"]').click(); + } + + async clickAddRedChartsToDashboard() { + await this.openMoreActionsMenu(); + await this.page.locator('[data-test="traces-add-red-to-dashboard-btn"]').click(); + } + + async createAlertFromChartMenu(title) { + const panel = this.metricsPanelLocator(title); + if (title === 'Duration') { + await panel.hover(); + await this.page.locator('[data-test="traces-duration-actions-btn"]').click(); + await this.page.locator('[data-test="traces-duration-create-alert"]').click(); + return; + } + // Hovering the chart body opens its tooltip, which then covers this button. + const actionsBtn = panel.locator('[data-test="panel-explore-actions-btn"]'); + await actionsBtn.hover(); + await actionsBtn.click(); + await this.page.locator('[data-test="panel-explore-create-alert"]').click(); + } + + async expectAddToDashboardPanels(count) { + const dialog = this.page.locator('[data-test="add-to-dashboard-dialog"]'); + await expect(dialog).toBeVisible({ timeout: 15000 }); + await expect(this.page.locator('[data-test="add-to-dashboard-notice"]')).toBeVisible(); + await expect(this.page.locator('[data-test="add-to-dashboard-panel-title"]')).toHaveCount(count); + } + + async selectDashboardInAddDialog(title) { + await this.page.locator('[data-test="dashboard-dropdown-dashboard-selection-trigger"]').click(); + await this.page + .locator('[data-test="dashboard-dropdown-dashboard-selection-option"]') + .filter({ hasText: title }) + .first() + .click(); + } + + async confirmAddToDashboard() { + await this.page + .locator('[data-test="add-to-dashboard-dialog"] [data-test="o-dialog-primary-btn"]') + .click(); + } + + async expectAlertFormPrefilledFromTraces() { + await expect(this.page).toHaveURL(/prefill=traces/, { timeout: 30000 }); + await expect( + this.page.locator('[data-test="add-alert-stream-type-select-dropdown"]').first() + ).toContainText('traces', { timeout: 30000 }); + await expect( + this.page.locator('[data-test="alert-trigger-threshold-input-field"]').first() + ).toHaveValue('1'); + await expect( + this.page.locator('[data-test="alert-trigger-operator-select"]').first() + ).toContainText('>='); + } } diff --git a/tests/ui-testing/playwright-tests/RUM-Analytics/rum-product-analytics.spec.js b/tests/ui-testing/playwright-tests/RUM-Analytics/rum-product-analytics.spec.js index adb994eb34..7aeb45850e 100644 --- a/tests/ui-testing/playwright-tests/RUM-Analytics/rum-product-analytics.spec.js +++ b/tests/ui-testing/playwright-tests/RUM-Analytics/rum-product-analytics.spec.js @@ -914,7 +914,9 @@ test.describe('RUM Product Analytics', () => { await expect(pa.addToDashboardDialog).toBeVisible(); await expect(pa.addToDashboardNotice).toBeVisible(); await pa.pickOption('dashboard-dropdown-dashboard-selection', { label: title, search: title }); - await page.locator('[data-test="metrics-new-dashboard-panel-title-field"]').fill('Funnel e2e'); + const panelTitles = pa.addToDashboardDialog.locator('[data-test="add-to-dashboard-panel-title"]'); + await expect(panelTitles).toHaveCount(1); + await expect(panelTitles.first()).toContainText('Funnel:'); const add = pa.addToDashboardDialog.locator('[data-test="o-dialog-primary-btn"]'); await expect(add).toBeEnabled({ timeout: 15000 }); await add.click(); diff --git a/tests/ui-testing/playwright-tests/Traces/tracesRedActions.spec.js b/tests/ui-testing/playwright-tests/Traces/tracesRedActions.spec.js new file mode 100644 index 0000000000..fffa88e4a1 --- /dev/null +++ b/tests/ui-testing/playwright-tests/Traces/tracesRedActions.spec.js @@ -0,0 +1,96 @@ +// tracesRedActions.spec.js — Create alert and Add to dashboard from the Traces RED charts and the ≡ menu. + +const { test, expect, navigateToBase } = require('../utils/enhanced-baseFixtures.js'); +const testLogger = require('../utils/test-logger.js'); +const PageManager = require('../../pages/page-manager.js'); +const { ingestTraces } = require('../utils/trace-ingestion.js'); + +const RED_TITLE_PREFIXES = ['Rate · ', 'Errors · ', 'Duration · ']; + +test.describe('Traces RED actions', () => { + let pm; + + // The Errors seed query needs at least one error span in the window. + test.beforeAll(async ({ browser }) => { + test.setTimeout(120000); + const context = await browser.newContext({ + storageState: 'playwright-tests/utils/auth/user.json', + }); + const page = await context.newPage(); + try { + await ingestTraces(page, 6, { forceScenario: 'error' }); + await ingestTraces(page, 10, { forceScenario: 'success' }); + } catch (e) { + testLogger.warn('RED actions trace seeding failed (continuing)', { error: e.message }); + } finally { + await context.close(); + } + }); + + test.beforeEach(async ({ page }, testInfo) => { + testLogger.testStart(testInfo.title, testInfo.file); + await navigateToBase(page); + pm = new PageManager(page); + await page.waitForLoadState('networkidle'); + await pm.tracesPage.navigateToTraces(); + await pm.tracesPage.isStreamSelectVisible(); + await pm.tracesPage.selectTraceStream('default'); + }); + + test.afterEach(async ({}, testInfo) => { + testLogger.testEnd(testInfo.title, testInfo.status); + }); + + async function searchAndShowCharts() { + await pm.tracesPage.setupTraceSearch(); + await pm.tracesPage.waitForTraceSearchResults(); + expect(await pm.tracesPage.hasTraceResults(), 'Trace results must be available').toBeTruthy(); + expect(await pm.tracesPage.ensureMetricsDashboardVisible(), 'RED charts must show').toBeTruthy(); + const rendered = await pm.tracesPage.waitForMetricsPanels(); + expect(rendered, `Panels rendered: ${rendered.join(', ')}`).toContain('Errors'); + } + + test('Errors ⋮ → Create alert opens the alert form with a traces HAVING query and a ≥ 1 row trigger', { + tag: ['@tracesRedActions', '@traces', '@P0', '@all'], + }, async ({ page }) => { + await searchAndShowCharts(); + + await pm.tracesPage.createAlertFromChartMenu('Errors'); + + await pm.createAlertFromSourceDialog.expectDialogVisible(); + const preview = pm.createAlertFromSourceDialog.queryPreview; + await expect(preview).toBeVisible({ timeout: 15000 }); + await expect(preview).toContainText(/HAVING/i); + await expect(preview).toContainText("span_status = 'ERROR'"); + + await pm.createAlertFromSourceDialog.clickContinue(); + await pm.tracesPage.expectAlertFormPrefilledFromTraces(); + }); + + test('≡ → Add RED charts to dashboard adds Rate, Errors and Duration panels', { + tag: ['@tracesRedActions', '@traces', '@P0', '@all'], + }, async ({ page }) => { + const dashboardTitle = `traces_red_${Date.now()}`; + const { dashboardId, folderId } = await pm.apiCleanup.createDashboardWithPanel( + dashboardTitle, + 'seed', + 'e2e_automate', + ); + + try { + await searchAndShowCharts(); + + await pm.tracesPage.clickAddRedChartsToDashboard(); + await pm.tracesPage.expectAddToDashboardPanels(3); + await pm.tracesPage.selectDashboardInAddDialog(dashboardTitle); + await pm.tracesPage.confirmAddToDashboard(); + + await expect(page).toHaveURL(new RegExp(`dashboard=${dashboardId}`), { timeout: 30000 }); + for (const prefix of RED_TITLE_PREFIXES) { + await expect(page.getByText(prefix, { exact: false }).first()).toBeVisible({ timeout: 30000 }); + } + } finally { + await pm.apiCleanup.deleteDashboard(dashboardId, folderId); + } + }); +}); diff --git a/web/src/components/CodeQueryEditor.spec.ts b/web/src/components/CodeQueryEditor.spec.ts index e982a62ec4..4633239b57 100644 --- a/web/src/components/CodeQueryEditor.spec.ts +++ b/web/src/components/CodeQueryEditor.spec.ts @@ -153,6 +153,7 @@ vi.mock("@/composables/useLogs/searchState", () => ({ const nlq = vi.hoisted(() => ({ generateSQL: vi.fn(() => Promise.resolve(null as string | null)), + transformToSQL: vi.fn((_nl: string, sql: string) => `-- nl\n${sql}`), refusalNotice: null as unknown as { value: string }, showErrorNotification: vi.fn(), })); @@ -165,7 +166,7 @@ vi.mock("@/composables/useNLQuery", async () => { useNLQuery: () => ({ detectNaturalLanguage: vi.fn(() => false), generateSQL: nlq.generateSQL, - transformToSQL: vi.fn((_nl: string, sql: string) => sql), + transformToSQL: nlq.transformToSQL, isGenerating: { value: false }, streamingResponse: { value: "" }, refusalNotice: nlq.refusalNotice, @@ -485,6 +486,46 @@ describe("CodeQueryEditor", () => { }); }); + describe("generated query transform", () => { + const sql = "SELECT * FROM default WHERE a = 1 LIMIT 10"; + const lastQuery = (wrapper: ReturnType) => + wrapper.emitted("update:query")?.at(-1)?.[0]; + + it("writes the commented SQL by default, as Logs expects", async () => { + nlq.generateSQL.mockResolvedValueOnce(sql); + const wrapper = createWrapper(); + await wrapper.vm.handleGenerateSQL("rows where a is 1"); + expect(lastQuery(wrapper)).toBe(`-- nl\n${sql}`); + expect(wrapper.emitted("generation-success")?.[0]?.[0]).toEqual({ + type: "sql", + message: sql, + }); + wrapper.unmount(); + }); + + it("writes the page's transform of the SQL instead", async () => { + nlq.generateSQL.mockResolvedValueOnce(sql); + nlq.transformToSQL.mockClear(); + const transformGeneratedQuery = vi.fn(() => "a = 1"); + const wrapper = createWrapper({ transformGeneratedQuery }); + await wrapper.vm.handleGenerateSQL("rows where a is 1"); + expect(transformGeneratedQuery).toHaveBeenCalledWith(sql); + expect(nlq.transformToSQL).not.toHaveBeenCalled(); + expect(lastQuery(wrapper)).toBe("a = 1"); + expect(wrapper.emitted("generation-success")).toHaveLength(1); + wrapper.unmount(); + }); + + it("leaves the editor alone when the transform returns null", async () => { + nlq.generateSQL.mockResolvedValueOnce(sql); + const wrapper = createWrapper({ transformGeneratedQuery: () => null }); + await wrapper.vm.handleGenerateSQL("count errors per service"); + expect(wrapper.emitted("update:query")).toBeUndefined(); + expect(wrapper.emitted("generation-success")).toBeUndefined(); + wrapper.unmount(); + }); + }); + describe("Exposed Methods", () => { it("should expose setValue method", () => { const wrapper = createWrapper(); diff --git a/web/src/components/CodeQueryEditor.vue b/web/src/components/CodeQueryEditor.vue index aecaf3b356..70625fd742 100644 --- a/web/src/components/CodeQueryEditor.vue +++ b/web/src/components/CodeQueryEditor.vue @@ -491,6 +491,11 @@ export default defineComponent({ type: Boolean, default: false, }, + /** Replaces the default commented-SQL write of an AI result; null leaves the editor unchanged. */ + transformGeneratedQuery: { + type: Function as PropType<(sql: string) => string | null>, + default: null, + }, /** The range the field-value resolver searches, in microseconds; null means the default lookback. */ valuesTimeRange: { type: Object as PropType<{ startTime: number; endTime: number } | null>, @@ -716,7 +721,10 @@ export default defineComponent({ } // Normal query generation - transform and update editor with language-specific comments - const transformedText = transformToSQL(currentText, generatedSQL, props.language); + const transformedText = props.transformGeneratedQuery + ? props.transformGeneratedQuery(generatedSQL) + : transformToSQL(currentText, generatedSQL, props.language); + if (transformedText === null) return; // Update editor value setValue(transformedText); diff --git a/web/src/components/QueryEditor.spec.ts b/web/src/components/QueryEditor.spec.ts index 1bfbc80c9b..06d75e1ab8 100644 --- a/web/src/components/QueryEditor.spec.ts +++ b/web/src/components/QueryEditor.spec.ts @@ -68,6 +68,7 @@ const codeQueryEditorStub = { "editorId", "valuesTimeRange", "textPreview", + "transformGeneratedQuery", ], emits: [ "update:query", @@ -581,6 +582,29 @@ describe("QueryEditor", () => { }); }); + describe("page hooks for the code editor", () => { + it("names the editor from the prefix and language by default", () => { + wrapper = mountQueryEditor({ dataTestPrefix: "logs-search-bar" }); + expect(wrapper.findComponent(codeQueryEditorStub).props("editorId")).toBe( + "logs-search-bar-editor-sql", + ); + expect( + wrapper.findComponent(codeQueryEditorStub).props("transformGeneratedQuery"), + ).toBeUndefined(); + }); + + it("forwards a page's editor id and generated-query transform", () => { + const transform = (sql: string) => sql; + wrapper = mountQueryEditor({ + editorId: "traces-query-editor", + transformGeneratedQuery: transform, + }); + const editor = wrapper.findComponent(codeQueryEditorStub); + expect(editor.props("editorId")).toBe("traces-query-editor"); + expect(editor.props("transformGeneratedQuery")).toBe(transform); + }); + }); + describe("values time range", () => { it("forwards the range to the code editor", () => { const range = { startTime: 1, endTime: 2 }; diff --git a/web/src/components/QueryEditor.vue b/web/src/components/QueryEditor.vue index 1e0898fce4..8c275c3934 100644 --- a/web/src/components/QueryEditor.vue +++ b/web/src/components/QueryEditor.vue @@ -85,7 +85,7 @@ string | null) | null; // Page-specific write of an AI result (null result = keep the editor) // NL Mode (optional external control) nlpMode?: boolean; // External NLP mode control (undefined = internal control) @@ -208,6 +210,7 @@ interface Props { // Testing dataTestPrefix?: string; + editorId?: string; // Monaco host id; defaults to `-editor-` } const props = withDefaults(defineProps(), { diff --git a/web/src/components/dashboards/PanelContainer.spec.ts b/web/src/components/dashboards/PanelContainer.spec.ts index 8e2101a7db..4f41b401ad 100644 --- a/web/src/components/dashboards/PanelContainer.spec.ts +++ b/web/src/components/dashboards/PanelContainer.spec.ts @@ -2931,4 +2931,53 @@ describe("PanelContainer", () => { expect(wrapper.find(button).exists()).toBe(shown); }); }); + + describe("Explore actions", () => { + const menuStubs = { + ODropdown: { + name: "ODropdown", + template: '
', + }, + ODropdownItem: { + name: "ODropdownItem", + emits: ["select"], + template: + '
', + }, + }; + const trigger = '[data-test="panel-explore-actions-btn"]'; + + it("shows the explore menu only with viewOnly and exploreActions", () => { + wrapper = createWrapper({ viewOnly: true, exploreActions: true }, menuStubs); + expect(wrapper.find(trigger).exists()).toBe(true); + }); + + it.each([ + [{ viewOnly: false, exploreActions: true }], + [{ viewOnly: true, exploreActions: false }], + [{ viewOnly: true }], + ])("hides the explore menu for %o", (props) => { + wrapper = createWrapper(props, menuStubs); + expect(wrapper.find(trigger).exists()).toBe(false); + expect(wrapper.find('[data-test="panel-explore-create-alert"]').exists()).toBe(false); + }); + + it("never shows next to the panel's own menu", () => { + wrapper = createWrapper({ viewOnly: false, exploreActions: true }, menuStubs); + expect(wrapper.find('[data-test="dashboard-edit-panel"]').exists()).toBe(true); + expect(wrapper.find('[data-test="panel-explore-add-to-dashboard"]').exists()).toBe(false); + }); + + it("emits exploreCreateAlert with the panel schema", async () => { + wrapper = createWrapper({ viewOnly: true, exploreActions: true }, menuStubs); + await wrapper.find('[data-test="panel-explore-create-alert"]').trigger("click"); + expect(wrapper.emitted("exploreCreateAlert")).toEqual([[mockPanelData]]); + }); + + it("emits exploreAddToDashboard with the panel schema", async () => { + wrapper = createWrapper({ viewOnly: true, exploreActions: true }, menuStubs); + await wrapper.find('[data-test="panel-explore-add-to-dashboard"]').trigger("click"); + expect(wrapper.emitted("exploreAddToDashboard")).toEqual([[mockPanelData]]); + }); + }); }); diff --git a/web/src/components/dashboards/PanelContainer.vue b/web/src/components/dashboards/PanelContainer.vue index 3cb1fc531a..c4ff1b2186 100644 --- a/web/src/components/dashboards/PanelContainer.vue +++ b/web/src/components/dashboards/PanelContainer.vue @@ -348,6 +348,33 @@ along with this program. If not, see . > + + + + + {{ t("dashboard.panelContainer.exploreCreateAlert") }} + + + {{ t("dashboard.panelContainer.exploreAddToDashboard") }} + +