Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import type {
ObservabilityAgentBuilderPluginStart,
} from '../types';
import { OBSERVABILITY_AGENT_TOOL_IDS } from '../tools/register_tools';
import { OBSERVABILITY_GET_ALERTS_TOOL_ID } from '../tools';
import { getAgentBuilderResourceAvailability } from '../utils/get_agent_builder_resource_availability';

export const OBSERVABILITY_AGENT_ID = 'observability.agent';
Expand Down Expand Up @@ -41,16 +40,7 @@ export async function registerObservabilityAgent({
},
},
configuration: {
instructions:
'You are an observability specialist agent.\n' +
'\n' +
`### OUTPUT STYLE for ALERTS\n` +
`- When alerts results are provided (e.g., from \`${OBSERVABILITY_GET_ALERTS_TOOL_ID}\`), respond with a concise Markdown table.\n` +
`- Use only the \`selectedFields\` metadata to define up to 5 columns for the table. Do **NOT** pick more than 5 fields.\n` +
`- When choosing fields for the columns, choose fields that are most relevant to the user's request and conversation context.\n` +
`- Generate human-friendly column names by converting dotted paths to Title Case and stripping common prefixes like \`kibana.alert.\` or \`service.\`.\n` +
`- Leave cells blank when values are missing.\n` +
`- Always add a summary of the results in addition to the table. Mention the total number of alerts in the summary.`,
instructions: 'You are an observability specialist agent.\n',
tools: [
{
tool_ids: OBSERVABILITY_AGENT_TOOL_IDS,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* 2.0.
*/

import { omit } from 'lodash';
import type { CoreSetup, KibanaRequest, Logger } from '@kbn/core/server';
import { pick } from 'lodash';
import type { CoreSetup, KibanaRequest } from '@kbn/core/server';
import {
ALERT_STATUS,
ALERT_STATUS_ACTIVE,
Expand All @@ -17,70 +17,34 @@ import type {
ObservabilityAgentBuilderPluginStart,
ObservabilityAgentBuilderPluginStartDependencies,
} from '../../types';
import { getRelevantAlertFields } from './get_relevant_alert_fields';
import { getTotalHits } from '../../utils/get_total_hits';
import { kqlFilter as buildKqlFilter } from '../../utils/dsl_filters';
import { getDefaultConnectorId } from '../../utils/get_default_connector_id';

const OMITTED_ALERT_FIELDS = [
'event.action',
'event.kind',
'kibana.alert.rule.execution.uuid',
'kibana.alert.rule.revision',
'kibana.alert.rule.tags',
'kibana.alert.rule.uuid',
'kibana.alert.workflow_status',
'kibana.space_ids',
'kibana.alert.time_range',
'kibana.version',
] as const;
import { defaultFields } from './tool';

export async function getToolHandler({
core,
request,
logger,
start,
end,
query,
kqlFilter,
includeRecovered,
fields,
}: {
core: CoreSetup<
ObservabilityAgentBuilderPluginStartDependencies,
ObservabilityAgentBuilderPluginStart
>;
request: KibanaRequest;
logger: Logger;
start: string;
end: string;
query: string;
kqlFilter?: string;
includeRecovered?: boolean;
fields?: string[];
}) {
const [coreStart, pluginStart] = await core.getStartServices();
const { inference, ruleRegistry } = pluginStart;
const [, pluginStart] = await core.getStartServices();
const { ruleRegistry } = pluginStart;

const alertsClient = await ruleRegistry.getRacClientWithRequest(request);
const connectorId = await getDefaultConnectorId({
coreStart,
inference,
request,
logger,
});

const boundInferenceClient = inference.getClient({
request,
bindTo: { connectorId },
});

const selectedFields = await getRelevantAlertFields({
coreStart,
pluginStart,
request,
inferenceClient: boundInferenceClient,
logger,
query,
});

const response = await alertsClient.find({
ruleTypeIds: OBSERVABILITY_RULE_TYPE_IDS_WITH_SUPPORTED_STACK_RULE_TYPES,
Expand Down Expand Up @@ -113,7 +77,8 @@ export async function getToolHandler({
});

const total = getTotalHits(response);
const alerts = response.hits.hits.map((hit) => omit(hit._source ?? {}, ...OMITTED_ALERT_FIELDS));
const fieldsToReturn = fields ?? defaultFields;
const alerts = response.hits.hits.map((hit) => pick(hit._source ?? {}, fieldsToReturn));

return { alerts, selectedFields, total };
return { alerts, total };
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ export const defaultFields = [
'kibana.alert.instance.id',
'kibana.alert.reason',
'kibana.alert.rule.category',
'kibana.alert.rule.consumer',
'kibana.alert.rule.name',
'kibana.alert.rule.rule_type_id',
'kibana.alert.rule.tags',
'kibana.alert.start',
'kibana.alert.status',
'kibana.alert.evaluation.threshold',
'kibana.alert.time_range.gte',
'kibana.alert.time_range.lte',
'kibana.alert.workflow_status',
Expand All @@ -54,14 +56,24 @@ export const defaultFields = [

const getAlertsSchema = z.object({
...timeRangeSchemaOptional(DEFAULT_TIME_RANGE),
query: z.string().min(1).describe('Natural language query to guide relevant field selection.'),
kqlFilter: z.string().optional().describe('Filter alerts by field:value pairs'),
kqlFilter: z
.string()
.optional()
.describe(
'Optional KQL (Kibana Query Language) filter to narrow down alerts. Examples: \'service.name: "frontend"\' (alerts for the frontend service), \'service.name: "checkout" AND host.name: "web-*"\', \'kibana.alert.rule.name: "High CPU"\'.'
),
includeRecovered: z
.boolean()
.optional()
.describe(
'Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned.'
),
fields: z
.array(z.string())
.optional()
.describe(
'Optional list of fields to include in the alert documents. If not specified, a default set of common alert fields is returned. Use this to request specific fields like "error.message", "url.full", or any custom alert fields.'
),
});

export function createGetAlertsTool({
Expand Down Expand Up @@ -99,19 +111,18 @@ Supports filtering by status (active/recovered) and KQL queries.`,
end = DEFAULT_TIME_RANGE.end,
kqlFilter,
includeRecovered,
query,
fields,
} = toolParams;

try {
const { alerts, selectedFields, total } = await getToolHandler({
const { alerts, total } = await getToolHandler({
core,
request,
logger,
start,
end,
query,
kqlFilter,
includeRecovered,
fields,
});

return {
Expand All @@ -121,7 +132,6 @@ Supports filtering by status (active/recovered) and KQL queries.`,
data: {
total,
alerts,
selectedFields: selectedFields.length === 0 ? defaultFields : selectedFields,
},
},
],
Expand Down
Loading