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}, - }); - }} - /> - ) - } - /> - ); -}