From cb269b30c82809c4b587d98c374a30eb2d4098a9 Mon Sep 17 00:00:00 2001
From: Ogi <86684834+obostjancic@users.noreply.github.com>
Date: Thu, 22 May 2025 16:09:34 +0200
Subject: [PATCH 1/4] feat(agents-insights): traffic, duration, issues widgets
---
.../components/durationWidget.tsx | 97 +++++++++++++++++++
.../insights/agentMonitoring/utils/query.tsx | 5 +
.../views/agentsOverviewPage.tsx | 14 ++-
3 files changed, 113 insertions(+), 3 deletions(-)
create mode 100644 static/app/views/insights/agentMonitoring/components/durationWidget.tsx
create mode 100644 static/app/views/insights/agentMonitoring/utils/query.tsx
diff --git a/static/app/views/insights/agentMonitoring/components/durationWidget.tsx b/static/app/views/insights/agentMonitoring/components/durationWidget.tsx
new file mode 100644
index 00000000000000..80e134037c6aa4
--- /dev/null
+++ b/static/app/views/insights/agentMonitoring/components/durationWidget.tsx
@@ -0,0 +1,97 @@
+import {useMemo} from 'react';
+
+import {openInsightChartModal} from 'sentry/actionCreators/modal';
+import {t} from 'sentry/locale';
+import useOrganization from 'sentry/utils/useOrganization';
+import {Line} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/line';
+import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
+import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
+import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode';
+import {getAgentRunsFilter} from 'sentry/views/insights/agentMonitoring/utils/query';
+import {ChartType} from 'sentry/views/insights/common/components/chart';
+import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types';
+import {useEAPSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
+import {convertSeriesToTimeseries} from 'sentry/views/insights/common/utils/convertSeriesToTimeseries';
+import {Referrer} from 'sentry/views/insights/pages/platform/laravel/referrers';
+import {usePageFilterChartParams} from 'sentry/views/insights/pages/platform/laravel/utils';
+import {WidgetVisualizationStates} from 'sentry/views/insights/pages/platform/laravel/widgetVisualizationStates';
+import {useReleaseBubbleProps} from 'sentry/views/insights/pages/platform/shared/getReleaseBubbleProps';
+import {ModalChartContainer} from 'sentry/views/insights/pages/platform/shared/styles';
+import {Toolbar} from 'sentry/views/insights/pages/platform/shared/toolbar';
+import {useTransactionNameQuery} from 'sentry/views/insights/pages/platform/shared/useTransactionNameQuery';
+
+export default function DurationWidget(props: LoadableChartWidgetProps) {
+ const organization = useOrganization();
+ const {query} = useTransactionNameQuery();
+ const pageFilterChartParams = usePageFilterChartParams({
+ pageFilters: props.pageFilters,
+ });
+ const releaseBubbleProps = useReleaseBubbleProps(props);
+
+ const fullQuery = `${getAgentRunsFilter()} ${query}`.trim();
+
+ const {data, isLoading, error} = useEAPSeries(
+ {
+ ...pageFilterChartParams,
+ search: fullQuery,
+ yAxis: ['avg(span.duration)', 'p95(span.duration)'],
+ referrer: Referrer.DURATION_CHART,
+ },
+ Referrer.DURATION_CHART,
+ props.pageFilters
+ );
+
+ const plottables = useMemo(() => {
+ return Object.keys(data).map(key => {
+ const series = data[key as keyof typeof data];
+ return new Line(convertSeriesToTimeseries(series));
+ });
+ }, [data]);
+
+ const isEmpty = plottables.every(plottable => plottable.isEmpty);
+
+ const visualization = (
+
+ );
+
+ return (
+ }
+ Visualization={visualization}
+ Actions={
+ organization.features.includes('visibility-explore-view') &&
+ !isEmpty && (
+ {
+ openInsightChartModal({
+ title: t('Duration'),
+ children: {visualization},
+ });
+ }}
+ />
+ )
+ }
+ />
+ );
+}
diff --git a/static/app/views/insights/agentMonitoring/utils/query.tsx b/static/app/views/insights/agentMonitoring/utils/query.tsx
new file mode 100644
index 00000000000000..a28dacdd7b2284
--- /dev/null
+++ b/static/app/views/insights/agentMonitoring/utils/query.tsx
@@ -0,0 +1,5 @@
+const AI_PIPELINE_OPS = ['ai.pipeline.generateText', 'ai.pipeline.generateObject'];
+
+export const getAgentRunsFilter = () => {
+ return `span.op:[${AI_PIPELINE_OPS.join(',')}]`;
+};
diff --git a/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx b/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx
index 0f449a66e6c003..9891c39d8920c9 100644
--- a/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx
+++ b/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx
@@ -14,6 +14,7 @@ import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
import {limitMaxPickableDays} from 'sentry/views/explore/utils';
+import DurationWidget from 'sentry/views/insights/agentMonitoring/components/durationWidget';
import {ModelsTable} from 'sentry/views/insights/agentMonitoring/components/modelsTable';
import {ToolsTable} from 'sentry/views/insights/agentMonitoring/components/toolsTable';
import {TracesTable} from 'sentry/views/insights/agentMonitoring/components/tracesTable';
@@ -22,13 +23,16 @@ import {
useActiveTable,
} from 'sentry/views/insights/agentMonitoring/hooks/useActiveTable';
import {AgentInsightsFeature} from 'sentry/views/insights/agentMonitoring/utils/features';
+import {getAgentRunsFilter} from 'sentry/views/insights/agentMonitoring/utils/query';
import * as ModuleLayout from 'sentry/views/insights/common/components/moduleLayout';
import {ModulePageProviders} from 'sentry/views/insights/common/components/modulePageProviders';
import {ModuleBodyUpsellHook} from 'sentry/views/insights/common/components/moduleUpsellHookWrapper';
import {ToolRibbon} from 'sentry/views/insights/common/components/ribbon';
import {useOnboardingProject} from 'sentry/views/insights/common/queries/useOnboardingProject';
import {AgentsPageHeader} from 'sentry/views/insights/pages/agents/agentsPageHeader';
+import {IssuesWidget} from 'sentry/views/insights/pages/platform/shared/issuesWidget';
import {WidgetGrid} from 'sentry/views/insights/pages/platform/shared/styles';
+import {TrafficWidget} from 'sentry/views/insights/pages/platform/shared/trafficWidget';
import {useTransactionNameQuery} from 'sentry/views/insights/pages/platform/shared/useTransactionNameQuery';
import {ModuleName} from 'sentry/views/insights/types';
import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
@@ -76,13 +80,17 @@ function AgentsMonitoringPage() {
-
+
-
+
-
+
From a01f2cca2921b28778c2c8a6f34fdebfe8e8e8f9 Mon Sep 17 00:00:00 2001
From: Ogi <86684834+obostjancic@users.noreply.github.com>
Date: Fri, 23 May 2025 09:11:24 +0200
Subject: [PATCH 2/4] add comment
---
static/app/views/insights/agentMonitoring/utils/query.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/static/app/views/insights/agentMonitoring/utils/query.tsx b/static/app/views/insights/agentMonitoring/utils/query.tsx
index a28dacdd7b2284..aeb93efa7290a0 100644
--- a/static/app/views/insights/agentMonitoring/utils/query.tsx
+++ b/static/app/views/insights/agentMonitoring/utils/query.tsx
@@ -1,3 +1,5 @@
+// These are the span op we are currently ingesting.
+// They will probably change.
const AI_PIPELINE_OPS = ['ai.pipeline.generateText', 'ai.pipeline.generateObject'];
export const getAgentRunsFilter = () => {
From 20e156358c4f73205d149aa99c544b1b1a2365b0 Mon Sep 17 00:00:00 2001
From: Ogi <86684834+obostjancic@users.noreply.github.com>
Date: Fri, 23 May 2025 09:40:12 +0200
Subject: [PATCH 3/4] add widgets to chart loader
---
.../components/charts/chartWidgetLoader.tsx | 8 ++
.../views/agentsOverviewPage.tsx | 13 +--
.../overviewAgentsDurationChartWidget.tsx | 17 +++
.../widgets/overviewAgentsRunsChartWidget.tsx | 16 +++
.../platform/shared/baseLatencyWidget.tsx | 107 ++++++++++++++++++
5 files changed, 152 insertions(+), 9 deletions(-)
create mode 100644 static/app/views/insights/common/components/widgets/overviewAgentsDurationChartWidget.tsx
create mode 100644 static/app/views/insights/common/components/widgets/overviewAgentsRunsChartWidget.tsx
create mode 100644 static/app/views/insights/pages/platform/shared/baseLatencyWidget.tsx
diff --git a/static/app/components/charts/chartWidgetLoader.tsx b/static/app/components/charts/chartWidgetLoader.tsx
index 33e9b93cb70ef9..39a52e908db981 100644
--- a/static/app/components/charts/chartWidgetLoader.tsx
+++ b/static/app/components/charts/chartWidgetLoader.tsx
@@ -165,6 +165,14 @@ const CHART_MAP = {
),
overviewRequestsChartWidget: () =>
import('sentry/views/insights/common/components/widgets/overviewRequestsChartWidget'),
+ overviewAgentsRunsChartWidget: () =>
+ import(
+ 'sentry/views/insights/common/components/widgets/overviewAgentsRunsChartWidget'
+ ),
+ overviewAgentsDurationChartWidget: () =>
+ import(
+ 'sentry/views/insights/common/components/widgets/overviewAgentsDurationChartWidget'
+ ),
} satisfies Record Promise<{default: React.FC}>>;
/**
diff --git a/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx b/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx
index 9891c39d8920c9..a2038a1d97156f 100644
--- a/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx
+++ b/static/app/views/insights/agentMonitoring/views/agentsOverviewPage.tsx
@@ -14,7 +14,6 @@ import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
import {limitMaxPickableDays} from 'sentry/views/explore/utils';
-import DurationWidget from 'sentry/views/insights/agentMonitoring/components/durationWidget';
import {ModelsTable} from 'sentry/views/insights/agentMonitoring/components/modelsTable';
import {ToolsTable} from 'sentry/views/insights/agentMonitoring/components/toolsTable';
import {TracesTable} from 'sentry/views/insights/agentMonitoring/components/tracesTable';
@@ -23,16 +22,16 @@ import {
useActiveTable,
} from 'sentry/views/insights/agentMonitoring/hooks/useActiveTable';
import {AgentInsightsFeature} from 'sentry/views/insights/agentMonitoring/utils/features';
-import {getAgentRunsFilter} from 'sentry/views/insights/agentMonitoring/utils/query';
import * as ModuleLayout from 'sentry/views/insights/common/components/moduleLayout';
import {ModulePageProviders} from 'sentry/views/insights/common/components/modulePageProviders';
import {ModuleBodyUpsellHook} from 'sentry/views/insights/common/components/moduleUpsellHookWrapper';
import {ToolRibbon} from 'sentry/views/insights/common/components/ribbon';
+import OverviewAgentsDurationChartWidget from 'sentry/views/insights/common/components/widgets/overviewAgentsDurationChartWidget';
+import OverviewAgentsRunsChartWidget from 'sentry/views/insights/common/components/widgets/overviewAgentsRunsChartWidget';
import {useOnboardingProject} from 'sentry/views/insights/common/queries/useOnboardingProject';
import {AgentsPageHeader} from 'sentry/views/insights/pages/agents/agentsPageHeader';
import {IssuesWidget} from 'sentry/views/insights/pages/platform/shared/issuesWidget';
import {WidgetGrid} from 'sentry/views/insights/pages/platform/shared/styles';
-import {TrafficWidget} from 'sentry/views/insights/pages/platform/shared/trafficWidget';
import {useTransactionNameQuery} from 'sentry/views/insights/pages/platform/shared/useTransactionNameQuery';
import {ModuleName} from 'sentry/views/insights/types';
import {getTransactionSearchQuery} from 'sentry/views/performance/utils';
@@ -80,14 +79,10 @@ function AgentsMonitoringPage() {
-
+
-
+
diff --git a/static/app/views/insights/common/components/widgets/overviewAgentsDurationChartWidget.tsx b/static/app/views/insights/common/components/widgets/overviewAgentsDurationChartWidget.tsx
new file mode 100644
index 00000000000000..8eea64a25b30d3
--- /dev/null
+++ b/static/app/views/insights/common/components/widgets/overviewAgentsDurationChartWidget.tsx
@@ -0,0 +1,17 @@
+import {t} from 'sentry/locale';
+import {getAgentRunsFilter} from 'sentry/views/insights/agentMonitoring/utils/query';
+import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types';
+import BaseLatencyWidget from 'sentry/views/insights/pages/platform/shared/baseLatencyWidget';
+
+export default function OverviewAgentsDurationChartWidget(
+ props: LoadableChartWidgetProps
+) {
+ return (
+
+ );
+}
diff --git a/static/app/views/insights/common/components/widgets/overviewAgentsRunsChartWidget.tsx b/static/app/views/insights/common/components/widgets/overviewAgentsRunsChartWidget.tsx
new file mode 100644
index 00000000000000..ee1760ad806b30
--- /dev/null
+++ b/static/app/views/insights/common/components/widgets/overviewAgentsRunsChartWidget.tsx
@@ -0,0 +1,16 @@
+import {t} from 'sentry/locale';
+import {getAgentRunsFilter} from 'sentry/views/insights/agentMonitoring/utils/query';
+import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types';
+import {BaseTrafficWidget} from 'sentry/views/insights/pages/platform/shared/baseTrafficWidget';
+
+export default function OverviewAgentsRunsChartWidget(props: LoadableChartWidgetProps) {
+ return (
+
+ );
+}
diff --git a/static/app/views/insights/pages/platform/shared/baseLatencyWidget.tsx b/static/app/views/insights/pages/platform/shared/baseLatencyWidget.tsx
new file mode 100644
index 00000000000000..e7e4cb07045135
--- /dev/null
+++ b/static/app/views/insights/pages/platform/shared/baseLatencyWidget.tsx
@@ -0,0 +1,107 @@
+import {useMemo} from 'react';
+
+import {openInsightChartModal} from 'sentry/actionCreators/modal';
+import {t} from 'sentry/locale';
+import useOrganization from 'sentry/utils/useOrganization';
+import {Line} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/line';
+import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
+import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
+import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode';
+import {ChartType} from 'sentry/views/insights/common/components/chart';
+import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types';
+import {useEAPSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
+import {convertSeriesToTimeseries} from 'sentry/views/insights/common/utils/convertSeriesToTimeseries';
+import {Referrer} from 'sentry/views/insights/pages/platform/laravel/referrers';
+import {usePageFilterChartParams} from 'sentry/views/insights/pages/platform/laravel/utils';
+import {WidgetVisualizationStates} from 'sentry/views/insights/pages/platform/laravel/widgetVisualizationStates';
+import {useReleaseBubbleProps} from 'sentry/views/insights/pages/platform/shared/getReleaseBubbleProps';
+import {ModalChartContainer} from 'sentry/views/insights/pages/platform/shared/styles';
+import {Toolbar} from 'sentry/views/insights/pages/platform/shared/toolbar';
+import {useTransactionNameQuery} from 'sentry/views/insights/pages/platform/shared/useTransactionNameQuery';
+
+interface BaseLatencyWidgetProps extends LoadableChartWidgetProps {
+ title: string;
+ baseQuery?: string;
+}
+
+export default function BaseLatencyWidget({
+ title,
+ baseQuery,
+ ...props
+}: BaseLatencyWidgetProps) {
+ const organization = useOrganization();
+ const {query} = useTransactionNameQuery();
+ const pageFilterChartParams = usePageFilterChartParams({
+ pageFilters: props.pageFilters,
+ });
+ const releaseBubbleProps = useReleaseBubbleProps(props);
+
+ const fullQuery = `${baseQuery} ${query}`.trim();
+
+ const {data, isLoading, error} = useEAPSeries(
+ {
+ ...pageFilterChartParams,
+ search: fullQuery,
+ yAxis: ['avg(span.duration)', 'p95(span.duration)'],
+ referrer: Referrer.DURATION_CHART,
+ },
+ Referrer.DURATION_CHART,
+ props.pageFilters
+ );
+
+ const plottables = useMemo(() => {
+ return Object.keys(data).map(key => {
+ const series = data[key as keyof typeof data];
+ return new Line(convertSeriesToTimeseries(series));
+ });
+ }, [data]);
+
+ const isEmpty = plottables.every(plottable => plottable.isEmpty);
+
+ const visualization = (
+
+ );
+
+ return (
+ }
+ Visualization={visualization}
+ Actions={
+ organization.features.includes('visibility-explore-view') &&
+ !isEmpty && (
+ {
+ openInsightChartModal({
+ title: t('API Latency'),
+ children: {visualization},
+ });
+ }}
+ />
+ )
+ }
+ />
+ );
+}
From 029c0cda7c62529664dbdfbf3ab563c1f91d1fee Mon Sep 17 00:00:00 2001
From: Ogi <86684834+obostjancic@users.noreply.github.com>
Date: Fri, 23 May 2025 09:43:12 +0200
Subject: [PATCH 4/4] delete duration widget
---
.../components/durationWidget.tsx | 97 -------------------
1 file changed, 97 deletions(-)
delete mode 100644 static/app/views/insights/agentMonitoring/components/durationWidget.tsx
diff --git a/static/app/views/insights/agentMonitoring/components/durationWidget.tsx b/static/app/views/insights/agentMonitoring/components/durationWidget.tsx
deleted file mode 100644
index 80e134037c6aa4..00000000000000
--- a/static/app/views/insights/agentMonitoring/components/durationWidget.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import {useMemo} from 'react';
-
-import {openInsightChartModal} from 'sentry/actionCreators/modal';
-import {t} from 'sentry/locale';
-import useOrganization from 'sentry/utils/useOrganization';
-import {Line} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/line';
-import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
-import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
-import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode';
-import {getAgentRunsFilter} from 'sentry/views/insights/agentMonitoring/utils/query';
-import {ChartType} from 'sentry/views/insights/common/components/chart';
-import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types';
-import {useEAPSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
-import {convertSeriesToTimeseries} from 'sentry/views/insights/common/utils/convertSeriesToTimeseries';
-import {Referrer} from 'sentry/views/insights/pages/platform/laravel/referrers';
-import {usePageFilterChartParams} from 'sentry/views/insights/pages/platform/laravel/utils';
-import {WidgetVisualizationStates} from 'sentry/views/insights/pages/platform/laravel/widgetVisualizationStates';
-import {useReleaseBubbleProps} from 'sentry/views/insights/pages/platform/shared/getReleaseBubbleProps';
-import {ModalChartContainer} from 'sentry/views/insights/pages/platform/shared/styles';
-import {Toolbar} from 'sentry/views/insights/pages/platform/shared/toolbar';
-import {useTransactionNameQuery} from 'sentry/views/insights/pages/platform/shared/useTransactionNameQuery';
-
-export default function DurationWidget(props: LoadableChartWidgetProps) {
- const organization = useOrganization();
- const {query} = useTransactionNameQuery();
- const pageFilterChartParams = usePageFilterChartParams({
- pageFilters: props.pageFilters,
- });
- const releaseBubbleProps = useReleaseBubbleProps(props);
-
- const fullQuery = `${getAgentRunsFilter()} ${query}`.trim();
-
- const {data, isLoading, error} = useEAPSeries(
- {
- ...pageFilterChartParams,
- search: fullQuery,
- yAxis: ['avg(span.duration)', 'p95(span.duration)'],
- referrer: Referrer.DURATION_CHART,
- },
- Referrer.DURATION_CHART,
- props.pageFilters
- );
-
- const plottables = useMemo(() => {
- return Object.keys(data).map(key => {
- const series = data[key as keyof typeof data];
- return new Line(convertSeriesToTimeseries(series));
- });
- }, [data]);
-
- const isEmpty = plottables.every(plottable => plottable.isEmpty);
-
- const visualization = (
-
- );
-
- return (
- }
- Visualization={visualization}
- Actions={
- organization.features.includes('visibility-explore-view') &&
- !isEmpty && (
- {
- openInsightChartModal({
- title: t('Duration'),
- children: {visualization},
- });
- }}
- />
- )
- }
- />
- );
-}