Compare commits

...

1 Commits

Author SHA1 Message Date
Cursor Agent
d4ed725701 poc(ai-o11y): wire AI Observability quick filters on traces explorer
Add SignalType/QuickFiltersSource for ai_observability, map it to traces
data source, and forward type=builder_ai_query on fields keys/values APIs.
POC mounts the new signal on Traces Explorer to exercise the path.

Co-authored-by: Gaurav Tewari <gauravtewari111@gmail.com>
2026-08-04 09:40:09 +00:00
10 changed files with 70 additions and 16 deletions

View File

@@ -23,6 +23,7 @@ export const getKeySuggestions = (
fieldDataType = '',
signalSource = '',
metricNamespace = '',
type = '',
} = props;
const encodedSignal = encodeURIComponent(signal);
@@ -32,8 +33,11 @@ export const getKeySuggestions = (
const encodedFieldDataType = encodeURIComponent(fieldDataType);
const encodedSource = encodeURIComponent(signalSource);
const encodedMetricNamespace = encodeURIComponent(metricNamespace);
const encodedType = encodeURIComponent(type);
const typeParam = type ? `&type=${encodedType}` : '';
return axios.get(
`/fields/keys?signal=${encodedSignal}&searchText=${encodedSearchText}&metricName=${encodedMetricName}&fieldContext=${encodedFieldContext}&fieldDataType=${encodedFieldDataType}&source=${encodedSource}&metricNamespace=${encodedMetricNamespace}`,
`/fields/keys?signal=${encodedSignal}&searchText=${encodedSearchText}&metricName=${encodedMetricName}&fieldContext=${encodedFieldContext}&fieldDataType=${encodedFieldDataType}&source=${encodedSource}&metricNamespace=${encodedMetricNamespace}${typeParam}`,
);
};

View File

@@ -15,15 +15,16 @@ import {
export const getValueSuggestions = (
props: QueryKeyValueRequestProps,
): Promise<AxiosResponse<QueryKeyValueSuggestionsResponseProps>> => {
const { signal, key, searchText, signalSource, metricName } = props;
const { signal, key, searchText, signalSource, metricName, type } = props;
const encodedSignal = encodeURIComponent(signal);
const encodedKey = encodeURIComponent(key);
const encodedMetricName = encodeURIComponent(metricName || '');
const encodedSearchText = encodeURIComponent(searchText);
const encodedSource = encodeURIComponent(signalSource || '');
const typeParam = type ? `&type=${encodeURIComponent(type)}` : '';
return axios.get(
`/fields/values?signal=${encodedSignal}&name=${encodedKey}&searchText=${encodedSearchText}&metricName=${encodedMetricName}&source=${encodedSource}`,
`/fields/values?signal=${encodedSignal}&name=${encodedKey}&searchText=${encodedSearchText}&metricName=${encodedMetricName}&source=${encodedSource}${typeParam}`,
);
};

View File

@@ -27,6 +27,10 @@ function useCheckboxFilterValues({
searchText,
isOpen,
}: UseCheckboxFilterValuesProps): UseCheckboxFilterValuesReturn {
const usesFieldsValuesApi =
source === QuickFiltersSource.METER_EXPLORER ||
source === QuickFiltersSource.AI_OBSERVABILITY;
const { data, isLoading } = useGetAggregateValues(
{
aggregateOperator: filter.aggregateOperator || 'noop',
@@ -38,7 +42,7 @@ function useCheckboxFilterValues({
searchText: searchText ?? '',
},
{
enabled: isOpen && source !== QuickFiltersSource.METER_EXPLORER,
enabled: isOpen && !usesFieldsValuesApi,
keepPreviousData: true,
},
);
@@ -47,9 +51,14 @@ function useCheckboxFilterValues({
useGetQueryKeyValueSuggestions({
key: filter.attributeKey.key,
signal: filter.dataSource || DataSource.LOGS,
signalSource: 'meter',
signalSource: source === QuickFiltersSource.METER_EXPLORER ? 'meter' : '',
// POC: AI value suggestions — type=builder_ai_query (no suggestions for aggregates)
type:
source === QuickFiltersSource.AI_OBSERVABILITY
? 'builder_ai_query'
: undefined,
options: {
enabled: isOpen && source === QuickFiltersSource.METER_EXPLORER,
enabled: isOpen && usesFieldsValuesApi,
keepPreviousData: true,
},
});
@@ -57,7 +66,7 @@ function useCheckboxFilterValues({
const attributeValues: string[] = useMemo(() => {
const dataType = filter.attributeKey.dataType || DataTypes.String;
if (source === QuickFiltersSource.METER_EXPLORER && keyValueSuggestions) {
if (usesFieldsValuesApi && keyValueSuggestions) {
// Process the response data
const responseData = keyValueSuggestions?.data as any;
const values = responseData.data?.values || {};
@@ -88,7 +97,12 @@ function useCheckboxFilterValues({
return (data?.payload?.[key] || []).filter(
(val) => val !== undefined && val !== null,
);
}, [data?.payload, filter.attributeKey.dataType, keyValueSuggestions, source]);
}, [
data?.payload,
filter.attributeKey.dataType,
keyValueSuggestions,
usesFieldsValuesApi,
]);
return {
attributeValues,

View File

@@ -48,6 +48,11 @@ function OtherFilters({
() => signal && signal === SignalType.METER_EXPLORER,
[signal],
);
// POC: AI O11y quick-filter settings fetch keys with type=builder_ai_query
const isAiObservability = useMemo(
() => signal === SignalType.AI_OBSERVABILITY,
[signal],
);
const { data: suggestionsData, isFetching: isFetchingSuggestions } =
useGetAttributeSuggestions(
@@ -73,7 +78,8 @@ function OtherFilters({
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && !isLogDataSource && !isMeterDataSource,
enabled:
!!signal && !isLogDataSource && !isMeterDataSource && !isAiObservability,
},
);
@@ -82,11 +88,17 @@ function OtherFilters({
{
searchText: inputValue,
signal: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
signalSource: 'meter',
signalSource: isMeterDataSource ? 'meter' : '',
// POC: AI keys API — type=builder_ai_query&signal=traces
type: isAiObservability ? 'builder_ai_query' : undefined,
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && isMeterDataSource,
queryKey: [
REACT_QUERY_KEY.GET_OTHER_FILTERS,
inputValue,
isAiObservability ? 'builder_ai_query' : undefined,
],
enabled: !!signal && (isMeterDataSource || isAiObservability),
},
);
@@ -94,7 +106,7 @@ function OtherFilters({
let filterAttributes;
if (isLogDataSource) {
filterAttributes = suggestionsData?.payload?.attributes || [];
} else if (isMeterDataSource) {
} else if (isMeterDataSource || isAiObservability) {
const fieldKeys: QueryKeyDataSuggestionsProps[] = Object.values(
fieldKeysData?.data?.data?.keys || {},
)?.flat();
@@ -120,6 +132,7 @@ function OtherFilters({
isLogDataSource,
fieldKeysData,
isMeterDataSource,
isAiObservability,
]);
const handleAddFilter = (filter: FilterType): void => {

View File

@@ -7,4 +7,6 @@ export const SIGNAL_DATA_SOURCE_MAP = {
[SignalType.EXCEPTIONS]: DataSource.TRACES,
[SignalType.API_MONITORING]: DataSource.TRACES,
[SignalType.METER_EXPLORER]: DataSource.METRICS,
// POC: AI quick filters still resolve attribute keys against traces
[SignalType.AI_OBSERVABILITY]: DataSource.TRACES,
};

View File

@@ -24,6 +24,8 @@ export enum SignalType {
API_MONITORING = 'api_monitoring',
EXCEPTIONS = 'exceptions',
METER_EXPLORER = 'meter',
// POC: AI Observability explorer — maps to GET /orgs/me/filters/ai_observability
AI_OBSERVABILITY = 'ai_observability',
}
/**
@@ -68,6 +70,8 @@ export enum QuickFiltersSource {
API_MONITORING = 'api-monitoring',
EXCEPTIONS = 'exceptions',
METER_EXPLORER = 'meter',
// POC: AI Observability explorer surface
AI_OBSERVABILITY = 'ai-observability',
}
/**

View File

@@ -26,6 +26,7 @@ export const useGetQueryKeySuggestions: UseGetQueryKeySuggestions = (
fieldDataType,
metricName,
signalSource,
type,
}: QueryKeyRequestProps,
options?: UseQueryOptions<
AxiosResponse<QueryKeySuggestionsResponseProps>,
@@ -44,6 +45,7 @@ export const useGetQueryKeySuggestions: UseGetQueryKeySuggestions = (
fieldContext,
fieldDataType,
signalSource,
type,
];
}, [
options?.queryKey,
@@ -53,6 +55,7 @@ export const useGetQueryKeySuggestions: UseGetQueryKeySuggestions = (
fieldContext,
fieldDataType,
signalSource,
type,
]);
return useQuery<AxiosResponse<QueryKeySuggestionsResponseProps>, AxiosError>({
queryFn: () =>
@@ -63,6 +66,7 @@ export const useGetQueryKeySuggestions: UseGetQueryKeySuggestions = (
fieldContext,
fieldDataType,
signalSource,
type,
}),
...options,
queryKey,

View File

@@ -9,6 +9,7 @@ export const useGetQueryKeyValueSuggestions = ({
searchText,
signalSource,
metricName,
type,
options,
}: {
key: string;
@@ -20,6 +21,8 @@ export const useGetQueryKeyValueSuggestions = ({
AxiosError
>;
metricName?: string;
/** POC / AI O11y: forwarded as `type` on /fields/values */
type?: string;
}): UseQueryResult<
AxiosResponse<QueryKeyValueSuggestionsResponseProps>,
AxiosError
@@ -32,6 +35,7 @@ export const useGetQueryKeyValueSuggestions = ({
searchText,
signalSource,
metricName,
type,
],
queryFn: () =>
getValueSuggestions({
@@ -40,6 +44,7 @@ export const useGetQueryKeyValueSuggestions = ({
searchText: searchText || '',
signalSource: signalSource as 'meter' | '',
metricName: metricName || '',
type,
}),
...options,
});

View File

@@ -260,10 +260,13 @@ function TracesExplorer(): JSX.Element {
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<div className="trace-explorer-page">
<Card className="filter" hidden={!isOpen}>
{/* POC: AI Observability quick filters — signal hits /filters/ai_observability
and key/value suggestions send type=builder_ai_query */}
<QuickFilters
className="qf-traces-explorer"
source={QuickFiltersSource.TRACES_EXPLORER}
signal={SignalType.TRACES}
source={QuickFiltersSource.AI_OBSERVABILITY}
signal={SignalType.AI_OBSERVABILITY}
showQueryName={false}
handleFilterVisibilityChange={(): void => {
setOpen(!isOpen);
}}

View File

@@ -30,11 +30,13 @@ export interface QueryKeySuggestionsResponseProps {
export interface QueryKeyRequestProps {
signal: 'traces' | 'logs' | 'metrics';
searchText: string;
fieldContext?: 'resource' | 'scope' | 'attribute' | 'span';
fieldContext?: 'resource' | 'scope' | 'attribute' | 'span' | 'trace';
fieldDataType?: FieldDataType;
metricName?: string;
metricNamespace?: string;
signalSource?: 'meter' | '';
/** POC / AI O11y: e.g. 'builder_ai_query' — forwarded as `type` on /fields/keys */
type?: string;
}
export interface QueryKeyValueSuggestionsProps {
@@ -53,6 +55,8 @@ export interface QueryKeyValueRequestProps {
searchText: string;
signalSource?: 'meter' | '';
metricName?: string;
/** POC / AI O11y: e.g. 'builder_ai_query' — forwarded as `type` on /fields/values */
type?: string;
}
export type SignalType = 'traces' | 'logs' | 'metrics';