Skip to content

Commit 68231c0

Browse files
authored
feat(ai-generations): Add case sensitivity toggle to search (#103033)
Add toggle for case sensitivity of the search. Switch out underlying data fetching hooks to facilitate this new feature. <img width="113" height="115" alt="Screenshot 2025-11-10 at 10 30 11" src="https://github.com/user-attachments/assets/9931efa9-6c1c-42a9-a7cf-054b34545f3a" />
1 parent a3d487c commit 68231c0

File tree

3 files changed

+73
-51
lines changed

3 files changed

+73
-51
lines changed

static/app/views/insights/aiGenerations/views/components/generationsChart.tsx

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import {Fragment, useMemo} from 'react';
22

33
import {CompactSelect} from '@sentry/scraps/compactSelect';
44

5+
import {useCaseInsensitivity} from 'sentry/components/searchQueryBuilder/hooks';
56
import {IconClock} from 'sentry/icons/iconClock';
67
import {IconGraph} from 'sentry/icons/iconGraph';
78
import {t} from 'sentry/locale';
8-
import {useFetchSpanTimeSeries} from 'sentry/utils/timeSeries/useFetchEventsTimeSeries';
99
import type {TimeSeries} from 'sentry/views/dashboards/widgets/common/types';
1010
import {Area} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/area';
1111
import {Bars} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/bars';
@@ -14,18 +14,16 @@ import type {Plottable} from 'sentry/views/dashboards/widgets/timeSeriesWidget/p
1414
import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
1515
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
1616
import {useChartInterval} from 'sentry/views/explore/hooks/useChartInterval';
17+
import {useExploreTimeseries} from 'sentry/views/explore/hooks/useExploreTimeseries';
1718
import {
18-
useQueryParamsGroupBys,
1919
useQueryParamsVisualizes,
2020
useSetQueryParamsVisualizes,
2121
} from 'sentry/views/explore/queryParams/context';
2222
import type {Visualize} from 'sentry/views/explore/queryParams/visualize';
2323
import {useCombinedQuery} from 'sentry/views/insights/agents/hooks/useCombinedQuery';
2424
import {AI_GENERATIONS_PAGE_FILTER} from 'sentry/views/insights/aiGenerations/views/utils/constants';
25-
import {Referrer} from 'sentry/views/insights/aiGenerations/views/utils/referrer';
2625
import {ChartType} from 'sentry/views/insights/common/components/chart';
2726
import {WidgetVisualizationStates} from 'sentry/views/insights/pages/platform/laravel/widgetVisualizationStates';
28-
import type {SpanFields} from 'sentry/views/insights/types';
2927

3028
const CHART_TYPE_OPTIONS = [
3129
{
@@ -67,6 +65,15 @@ function prettifyAggregation(aggregation: string): string {
6765
export function GenerationsChart() {
6866
const visualizes = useQueryParamsVisualizes();
6967
const setVisualizes = useSetQueryParamsVisualizes();
68+
const [caseInsensitive] = useCaseInsensitivity();
69+
70+
const {result: timeseriesResult} = useExploreTimeseries({
71+
query: useCombinedQuery(AI_GENERATIONS_PAGE_FILTER),
72+
enabled: true,
73+
queryExtras: {
74+
caseInsensitive,
75+
},
76+
});
7077

7178
function handleChartTypeChange(index: number, chartType: ChartType) {
7279
const newVisualizes = visualizes.map((visualize, i) => {
@@ -86,6 +93,7 @@ export function GenerationsChart() {
8693
key={index}
8794
visualize={visualize}
8895
onChartTypeChange={chartType => handleChartTypeChange(index, chartType)}
96+
timeseriesResult={timeseriesResult}
8997
/>
9098
);
9199
})}
@@ -96,40 +104,19 @@ export function GenerationsChart() {
96104
function ChartWidget({
97105
visualize,
98106
onChartTypeChange,
107+
timeseriesResult,
99108
}: {
100109
onChartTypeChange: (chartType: ChartType) => void;
110+
timeseriesResult: ReturnType<typeof useExploreTimeseries>['result'];
101111
visualize: Visualize;
102112
}) {
103-
const groupBys = useQueryParamsGroupBys().filter(Boolean);
104113
const [interval, setInterval, intervalOptions] = useChartInterval();
105114
const chartType = visualize.chartType;
106115

107-
const query = useCombinedQuery(AI_GENERATIONS_PAGE_FILTER);
108-
const {data, isLoading, error} = useFetchSpanTimeSeries(
109-
{
110-
query,
111-
yAxis: [visualize.yAxis] as any,
112-
groupBy: groupBys as SpanFields[],
113-
interval,
114-
sort:
115-
groupBys.length > 0 && groupBys[0]
116-
? {
117-
field: groupBys[0],
118-
kind: 'desc' as const,
119-
}
120-
: undefined,
121-
topEvents: groupBys.length > 0 ? 5 : undefined,
122-
},
123-
Referrer.GENERATIONS_CHART
124-
);
125-
126116
const plottables = useMemo(() => {
127-
return (
128-
data?.timeSeries.map(
129-
timeSeries => new plottableConstructors[chartType](timeSeries)
130-
) ?? []
131-
);
132-
}, [chartType, data?.timeSeries]);
117+
const timeSeries = timeseriesResult.data[visualize.yAxis] ?? [];
118+
return timeSeries.map(series => new plottableConstructors[chartType](series)) ?? [];
119+
}, [chartType, timeseriesResult.data, visualize.yAxis]);
133120

134121
const isEmpty = useMemo(
135122
() => plottables.every(plottable => plottable.isEmpty),
@@ -170,8 +157,8 @@ function ChartWidget({
170157
revealActions="always"
171158
Visualization={
172159
<WidgetVisualizationStates
173-
isLoading={isLoading}
174-
error={error}
160+
isLoading={timeseriesResult.isLoading}
161+
error={timeseriesResult.error}
175162
isEmpty={isEmpty}
176163
VisualizationType={TimeSeriesWidgetVisualization}
177164
visualizationProps={{

static/app/views/insights/aiGenerations/views/components/generationsTable.tsx

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,23 @@ import {Container, Grid} from '@sentry/scraps/layout';
66
import {Text} from '@sentry/scraps/text';
77
import {Tooltip} from '@sentry/scraps/tooltip';
88

9+
import {useCaseInsensitivity} from 'sentry/components/searchQueryBuilder/hooks';
910
import {
1011
COL_WIDTH_UNDEFINED,
1112
type GridColumnOrder,
1213
} from 'sentry/components/tables/gridEditable';
1314
import TimeSince from 'sentry/components/timeSince';
1415
import {t} from 'sentry/locale';
16+
import type {NewQuery} from 'sentry/types/organization';
1517
import {getTimeStampFromTableDateField} from 'sentry/utils/dates';
18+
import EventView from 'sentry/utils/discover/eventView';
1619
import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
1720
import type {Sort} from 'sentry/utils/discover/fields';
21+
import {DiscoverDatasets} from 'sentry/utils/discover/types';
1822
import {getShortEventId} from 'sentry/utils/events';
1923
import {useLocation} from 'sentry/utils/useLocation';
2024
import useOrganization from 'sentry/utils/useOrganization';
25+
import usePageFilters from 'sentry/utils/usePageFilters';
2126
import {useTraceViewDrawer} from 'sentry/views/insights/agents/components/drawer';
2227
import {
2328
HeadSortCell,
@@ -34,7 +39,7 @@ import {
3439
import {Referrer} from 'sentry/views/insights/aiGenerations/views/utils/referrer';
3540
import {useFieldsQueryParam} from 'sentry/views/insights/aiGenerations/views/utils/useFieldsQueryParam';
3641
import {TextAlignRight} from 'sentry/views/insights/common/components/textAlign';
37-
import {useSpans} from 'sentry/views/insights/common/queries/useDiscover';
42+
import {useSpansQuery} from 'sentry/views/insights/common/queries/useSpansQuery';
3843
import {PlatformInsightsTable} from 'sentry/views/insights/pages/platform/shared/table';
3944
import {SpanFields} from 'sentry/views/insights/types';
4045

@@ -87,6 +92,8 @@ export function GenerationsTable() {
8792
const location = useLocation();
8893
const organization = useOrganization();
8994
const theme = useTheme();
95+
const {selection} = usePageFilters();
96+
const [caseInsensitive] = useCaseInsensitivity();
9097

9198
const fieldsToQuery = useMemo(() => {
9299
return [
@@ -97,36 +104,58 @@ export function GenerationsTable() {
97104
];
98105
}, [fields]);
99106

100-
const {data, meta, isLoading, error, pageLinks, isPlaceholderData} = useSpans(
101-
{
102-
search: query,
103-
fields: [...REQUIRED_FIELDS, ...fieldsToQuery] as any,
104-
cursor,
105-
sorts: [tableSort],
106-
keepPreviousData: true,
107-
limit: 20,
108-
},
109-
Referrer.GENERATIONS_TABLE
110-
);
107+
const eventView = useMemo(() => {
108+
const queryFields = [...REQUIRED_FIELDS, ...fieldsToQuery];
109+
110+
const discoverQuery: NewQuery = {
111+
id: undefined,
112+
name: 'AI Generations',
113+
fields: queryFields,
114+
orderby: [`${tableSort.kind === 'desc' ? '-' : ''}${tableSort.field}`],
115+
query,
116+
version: 2,
117+
dataset: DiscoverDatasets.SPANS,
118+
};
119+
120+
return EventView.fromNewQueryWithPageFilters(discoverQuery, selection);
121+
}, [fieldsToQuery, query, selection, tableSort.field, tableSort.kind]);
122+
123+
const {
124+
data = [],
125+
meta,
126+
isLoading,
127+
error,
128+
pageLinks,
129+
isPlaceholderData,
130+
} = useSpansQuery<Array<Record<string, any>>>({
131+
eventView,
132+
cursor,
133+
limit: 20,
134+
referrer: Referrer.GENERATIONS_TABLE,
135+
initialData: [],
136+
allowAggregateConditions: false,
137+
trackResponseAnalytics: false,
138+
queryExtras: {caseInsensitive},
139+
});
111140

112-
type TableData = (typeof data)[number];
141+
type TableData = Record<string, any>;
113142

114143
const renderBodyCell = useCallback(
115-
(column: GridColumnOrder<GenerationFields>, dataRow: TableData) => {
144+
(column: GridColumnOrder<string>, dataRow: TableData) => {
116145
if (column.key === SpanFields.ID) {
117146
return (
118147
<div>
119148
<Button
120149
priority="link"
121150
onClick={() => {
122151
openTraceViewDrawer(
123-
dataRow.trace!,
152+
dataRow.trace,
124153
dataRow.id,
125154
getTimeStampFromTableDateField(dataRow.timestamp)
126155
);
127156
}}
128157
>
129-
{getShortEventId(dataRow.id!)}
158+
{getShortEventId(dataRow.id)}
130159
</Button>
131160
</div>
132161
);
@@ -196,7 +225,7 @@ export function GenerationsTable() {
196225
if (column.key === SpanFields.TIMESTAMP) {
197226
return (
198227
<TextAlignRight>
199-
<TimeSince unitStyle="short" date={new Date(dataRow.timestamp!)} />
228+
<TimeSince unitStyle="short" date={new Date(dataRow.timestamp)} />
200229
</TextAlignRight>
201230
);
202231
}
@@ -212,7 +241,7 @@ export function GenerationsTable() {
212241
);
213242

214243
const renderHeadCell = useCallback(
215-
(column: GridColumnOrder<keyof TableData>) => {
244+
(column: GridColumnOrder<string>) => {
216245
return (
217246
<HeadSortCell
218247
align={column.key === SpanFields.TIMESTAMP ? 'right' : 'left'}
@@ -236,7 +265,7 @@ export function GenerationsTable() {
236265
isLoading={isLoading}
237266
error={error}
238267
initialColumnOrder={fields.map(
239-
(field): GridColumnOrder<GenerationFields> => ({
268+
(field): GridColumnOrder<string> => ({
240269
key: field,
241270
name: prettyFieldNames[field] ?? field,
242271
width: columnWidths[field] ?? COL_WIDTH_UNDEFINED,

static/app/views/insights/aiGenerations/views/overview.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
useEAPSpanSearchQueryBuilderProps,
1515
} from 'sentry/components/performance/spanSearchQueryBuilder';
1616
import {SearchQueryBuilderProvider} from 'sentry/components/searchQueryBuilder/context';
17+
import {useCaseInsensitivity} from 'sentry/components/searchQueryBuilder/hooks';
1718
import {IconChevron, IconEdit} from 'sentry/icons';
1819
import {t} from 'sentry/locale';
1920
import {getSelectedProjectList} from 'sentry/utils/project/useSelectedProjectsHaveField';
@@ -58,6 +59,7 @@ function AIGenerationsPage() {
5859
const [sidebarOpen, setSidebarOpen] = useState(true);
5960
const showOnboarding = useShowOnboarding();
6061
const datePageFilterProps = limitMaxPickableDays(organization);
62+
const [caseInsensitive, setCaseInsensitive] = useCaseInsensitivity();
6163

6264
const [searchQuery, setSearchQuery] = useQueryState(
6365
'query',
@@ -93,12 +95,16 @@ function AIGenerationsPage() {
9395
{key: 'trace', valuePattern: /^[0-9a-fA-F]{32}$/},
9496
{key: 'id', valuePattern: /^[0-9a-fA-F]{16}$/},
9597
],
98+
caseInsensitive,
99+
onCaseInsensitiveClick: setCaseInsensitive,
96100
}),
97101
[
102+
caseInsensitive,
98103
hasRawSearchReplacement,
99104
numberSecondaryAliases,
100105
numberTags,
101106
searchQuery,
107+
setCaseInsensitive,
102108
setSearchQuery,
103109
stringSecondaryAliases,
104110
stringTags,

0 commit comments

Comments
 (0)