diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/llm.ts b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/llm.ts index 787c4e85b1358..166a8db644870 100644 --- a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/llm.ts +++ b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/llm.ts @@ -11,6 +11,7 @@ import { LLM } from '@langchain/core/language_models/llms'; import { get } from 'lodash/fp'; import { v4 as uuidv4 } from 'uuid'; import { PublicMethodsOf } from '@kbn/utility-types'; +import type { TelemetryMetadata } from '@kbn/actions-plugin/server/lib'; import { DEFAULT_TIMEOUT, getDefaultArguments } from './constants'; import { getMessageContentAndRole } from './helpers'; @@ -28,6 +29,7 @@ interface ActionsClientLlmParams { timeout?: number; traceId?: string; traceOptions?: TraceOptions; + telemetryMetadata?: TelemetryMetadata; } export class ActionsClientLlm extends LLM { @@ -36,6 +38,7 @@ export class ActionsClientLlm extends LLM { #logger: Logger; #traceId: string; #timeout?: number; + telemetryMetadata?: TelemetryMetadata; // Local `llmType` as it can change and needs to be accessed by abstract `_llmType()` method // Not using getter as `this._llmType()` is called in the constructor via `super({})` @@ -54,6 +57,7 @@ export class ActionsClientLlm extends LLM { temperature, timeout, traceOptions, + telemetryMetadata, }: ActionsClientLlmParams) { super({ callbacks: [...(traceOptions?.tracers ?? [])], @@ -67,6 +71,7 @@ export class ActionsClientLlm extends LLM { this.#timeout = timeout; this.model = model; this.temperature = temperature; + this.telemetryMetadata = telemetryMetadata; } _llmType() { @@ -102,6 +107,7 @@ export class ActionsClientLlm extends LLM { model: this.model, messages: [assistantMessage], // the assistant message }, + telemetryMetadata: this.telemetryMetadata, }, } : { @@ -113,6 +119,7 @@ export class ActionsClientLlm extends LLM { ...getDefaultArguments(this.llmType, this.temperature), // This timeout is large because LangChain prompts can be complicated and take a long time timeout: this.#timeout ?? DEFAULT_TIMEOUT, + telemetryMetadata: this.telemetryMetadata, }, }, }; diff --git a/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_api_route.ts b/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_api_route.ts index f73d79ed28cad..862ac30cfaf51 100644 --- a/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_api_route.ts +++ b/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_api_route.ts @@ -77,6 +77,9 @@ export function registerApiAnalysisRoutes(router: IRouter { const response = await connector.performApiUnifiedCompletion({ body: { messages: [{ content: 'What is Elastic?', role: 'user' }] }, + telemetryMetadata: { pluginId: 'security_ai_assistant' }, }); expect(mockEsClient.transport.request).toBeCalledTimes(1); expect(mockEsClient.transport.request).toHaveBeenCalledWith( @@ -86,7 +87,13 @@ describe('InferenceConnector', () => { method: 'POST', path: '_inference/chat_completion/test/_stream', }, - { asStream: true, meta: true } + { + asStream: true, + meta: true, + headers: { + 'X-Elastic-Product-Use-Case': 'security_ai_assistant', + }, + } ); expect(response.choices[0].message.content).toEqual(' you'); }); @@ -290,7 +297,10 @@ describe('InferenceConnector', () => { method: 'POST', path: '_inference/chat_completion/test/_stream', }, - { asStream: true, meta: true } + { + asStream: true, + meta: true, + } ); }); @@ -312,7 +322,11 @@ describe('InferenceConnector', () => { method: 'POST', path: '_inference/chat_completion/test/_stream', }, - { asStream: true, meta: true, signal } + { + asStream: true, + meta: true, + signal, + } ); }); diff --git a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/inference/inference.ts b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/inference/inference.ts index 95fe053cd46ea..46c63814058c0 100644 --- a/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/inference/inference.ts +++ b/x-pack/platform/plugins/shared/stack_connectors/server/connector_types/inference/inference.ts @@ -196,6 +196,13 @@ export class InferenceConnector extends SubActionConnector { asStream: true, meta: true, signal: params.signal, + ...(params.telemetryMetadata?.pluginId + ? { + headers: { + 'X-Elastic-Product-Use-Case': params.telemetryMetadata?.pluginId, + }, + } + : {}), } ); // errors should be thrown as it will not be a stream response diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/helpers/get_evaluator_llm/index.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/helpers/get_evaluator_llm/index.ts index 236def9670d07..968a21122361a 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/helpers/get_evaluator_llm/index.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/helpers/get_evaluator_llm/index.ts @@ -61,5 +61,8 @@ export const getEvaluatorLlm = async ({ temperature: 0, // zero temperature for evaluation timeout: connectorTimeout, traceOptions, + telemetryMetadata: { + pluginId: 'security_attack_discovery', + }, }); }; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/index.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/index.ts index 2de3eb0bdf9d3..ce5b2ab71d177 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/index.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/attack_discovery/evaluation/index.ts @@ -92,6 +92,9 @@ export const evaluateAttackDiscovery = async ({ temperature: 0, // zero temperature for attack discovery, because we want structured JSON output timeout: connectorTimeout, traceOptions, + telemetryMetadata: { + pluginId: 'security_attack_discovery', + }, }); const graph = getDefaultAttackDiscoveryGraph({ diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts index da1d2244e5c5e..10039f1b1039d 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts @@ -89,6 +89,9 @@ export const callAssistantGraph: AgentExecutor = async ({ // failure could be due to bad connector, we should deliver that result to the client asap maxRetries: 0, convertSystemMessageToHumanContent: false, + telemetryMetadata: { + pluginId: 'security_ai_assistant', + }, }); const anonymizationFieldsRes = diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/invoke_attack_discovery_graph/index.tsx b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/invoke_attack_discovery_graph/index.tsx index 850bb06829d13..f0be667a81800 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/invoke_attack_discovery_graph/index.tsx +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/invoke_attack_discovery_graph/index.tsx @@ -87,6 +87,9 @@ export const invokeAttackDiscoveryGraph = async ({ temperature: 0, // zero temperature for attack discovery, because we want structured JSON output timeout: connectorTimeout, traceOptions, + telemetryMetadata: { + pluginId: 'security_attack_discovery', + }, }); if (llm == null) { diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts index 66363db3ae7fc..47fab77b675c8 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts @@ -156,6 +156,9 @@ export function getAssistantToolParams({ temperature: 0, // zero temperature because we want structured JSON output timeout: connectorTimeout, traceOptions, + telemetryMetadata: { + pluginId: 'security_defend_insights', + }, }); return { @@ -443,6 +446,9 @@ export const invokeDefendInsightsGraph = async ({ temperature: 0, timeout: connectorTimeout, traceOptions, + telemetryMetadata: { + pluginId: 'security_defend_insights', + }, }); if (llm == null) { diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts index ae82fec6ceeca..1056b9bb29a5b 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts @@ -250,6 +250,9 @@ export const postEvaluateRoute = ( streaming: false, maxRetries: 0, convertSystemMessageToHumanContent: false, + telemetryMetadata: { + pluginId: 'security_ai_assistant', + }, }); const llm = createLlmInstance(); const anonymizationFieldsRes =