diff --git a/package.json b/package.json index 4bc0286d9b1fb..dfef13c980dd2 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "resolutions": { "**/@babel/parser": "7.24.7", "**/@hello-pangea/dnd": "18.0.1", - "**/@langchain/core": "^0.3.57", + "**/@langchain/core": "^0.3.80", "**/@langchain/google-common": "^0.1.8", "**/@types/node": "22.19.1", "**/@types/prop-types": "15.7.5", @@ -1146,7 +1146,7 @@ "@kbn/zod-helpers": "link:src/platform/packages/shared/kbn-zod-helpers", "@langchain/aws": "^0.1.3", "@langchain/community": "^0.3.29", - "@langchain/core": "^0.3.57", + "@langchain/core": "^0.3.80", "@langchain/google-common": "^0.1.8", "@langchain/google-genai": "^0.1.8", "@langchain/google-vertexai": "^0.1.8", @@ -1301,7 +1301,7 @@ "jsonwebtoken": "^9.0.2", "jsts": "^1.6.2", "kea": "^2.6.0", - "langchain": "^0.3.15", + "langchain": "^0.3.37", "langsmith": "^0.3.7", "launchdarkly-js-client-sdk": "^3.8.1", "load-json-file": "^6.2.0", diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/chunks.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/chunks.ts index 3e7cb1ccf0350..09b071d1808f2 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/chunks.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/chunks.ts @@ -19,9 +19,9 @@ export const completionChunkToLangchain = (chunk: ChatCompletionChunkEvent): AIM const toolCallChunks = chunk.tool_calls.map((toolCall) => { return { index: toolCall.index, - id: toolCall.toolCallId, - name: toolCall.function.name, - args: toolCall.function.arguments, + id: toolCall.toolCallId || undefined, + name: toolCall.function.name || undefined, + args: toolCall.function.arguments || undefined, type: 'tool_call_chunk', }; }); diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.test.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.test.ts index 494a0b0986929..2f67e6cb55fa9 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.test.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.test.ts @@ -584,12 +584,9 @@ describe('InferenceChatModel', () => { { toolCallId: 'my-tool-call-id', index: 0, function: { name: '', arguments: '' } }, ], }, - { - tool_calls: [{ toolCallId: '', index: 0, function: { name: 'myfun', arguments: '' } }], - }, { tool_calls: [ - { toolCallId: '', index: 0, function: { name: 'ction', arguments: ' { "' } }, + { toolCallId: '', index: 0, function: { name: 'myfunction', arguments: ' { "' } }, ], }, { @@ -610,7 +607,8 @@ describe('InferenceChatModel', () => { concatChunk = concatChunk ? concatChunk.concat(chunk) : chunk; } - expect(allChunks.length).toBe(5); + expect(allChunks.length).toBe(4); + expect(concatChunk!.tool_calls).toEqual([ { id: 'my-tool-call-id', diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.ts index 61b7b078ef2f7..c27f43a1912c5 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.ts @@ -319,7 +319,10 @@ export class InferenceChatModel extends BaseChatModel { const schema: z.ZodType | Record = outputSchema; const name = config?.name; - const description = schema.description ?? 'A function available to call.'; + const description = + 'description' in schema && typeof schema.description === 'string' + ? schema.description + : 'A function available to call.'; const includeRaw = config?.includeRaw; let functionName = name ?? 'extract'; diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/index.ts b/x-pack/platform/packages/shared/kbn-langchain/server/index.ts index ebd1c0e5b49d4..71cffc3fd6eda 100644 --- a/x-pack/platform/packages/shared/kbn-langchain/server/index.ts +++ b/x-pack/platform/packages/shared/kbn-langchain/server/index.ts @@ -5,11 +5,8 @@ * 2.0. */ -import { ActionsClientBedrockChatModel } from './language_models/bedrock_chat'; import { ActionsClientChatOpenAI } from './language_models/chat_openai'; import { ActionsClientLlm } from './language_models/llm'; -import { ActionsClientSimpleChatModel } from './language_models/simple_chat_model'; -import { ActionsClientGeminiChatModel } from './language_models/gemini_chat'; import { ActionsClientChatVertexAI } from './language_models/chat_vertex'; import { ActionsClientChatBedrockConverse } from './language_models/chat_bedrock_converse'; import { parseBedrockStream } from './utils/bedrock'; @@ -20,11 +17,8 @@ export { parseBedrockStream, parseGeminiResponse, getDefaultArguments, - ActionsClientBedrockChatModel, ActionsClientChatOpenAI, ActionsClientChatVertexAI, - ActionsClientGeminiChatModel, ActionsClientLlm, - ActionsClientSimpleChatModel, ActionsClientChatBedrockConverse, }; diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/bedrock_chat.ts b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/bedrock_chat.ts deleted file mode 100644 index 8e84c3d37e17c..0000000000000 --- a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/bedrock_chat.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { BedrockChat as _BedrockChat } from '@langchain/community/chat_models/bedrock/web'; -import type { ActionsClient } from '@kbn/actions-plugin/server'; -import type { BaseChatModelParams } from '@langchain/core/language_models/chat_models'; -import type { Logger } from '@kbn/logging'; -import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { TelemetryMetadata } from '@kbn/actions-plugin/server/lib'; -import { prepareMessages, DEFAULT_BEDROCK_MODEL, DEFAULT_BEDROCK_REGION } from '../utils/bedrock'; - -export interface CustomChatModelInput extends BaseChatModelParams { - actionsClient: PublicMethodsOf; - connectorId: string; - logger: Logger; - temperature?: number; - signal?: AbortSignal; - model?: string; - maxTokens?: number; - telemetryMetadata?: TelemetryMetadata; -} - -/** - * @deprecated Use the ActionsClientChatBedrockConverse chat model instead. - * ActionsClientBedrockChatModel chat model supports non-streaming only the Bedrock Invoke API. - * The LangChain team will support only the Bedrock Converse API in the future. - */ -export class ActionsClientBedrockChatModel extends _BedrockChat { - constructor({ actionsClient, connectorId, logger, ...params }: CustomChatModelInput) { - super({ - ...params, - credentials: { accessKeyId: '', secretAccessKey: '' }, - // only needed to force BedrockChat to use messages api for Claude v2 - model: params.model ?? DEFAULT_BEDROCK_MODEL, - region: DEFAULT_BEDROCK_REGION, - fetchFn: async (url, options) => { - const inputBody = JSON.parse(options?.body as string); - - if (this.streaming) { - throw new Error( - `ActionsClientBedrockChat does not support streaming, use ActionsClientChatBedrockConverse instead` - ); - } - - const data = (await actionsClient.execute({ - actionId: connectorId, - params: { - subAction: 'invokeAIRaw', - subActionParams: { - telemetryMetadata: { - pluginId: params?.telemetryMetadata?.pluginId, - aggregateBy: params?.telemetryMetadata?.aggregateBy, - }, - messages: prepareMessages(inputBody.messages), - temperature: params.temperature ?? inputBody.temperature, - stopSequences: inputBody.stop_sequences, - system: inputBody.system, - maxTokens: params.maxTokens ?? inputBody.max_tokens, - tools: inputBody.tools, - anthropicVersion: inputBody.anthropic_version, - }, - }, - })) as { - status: string; - data: { message: string }; - message?: string; - serviceMessage?: string; - }; - if (data.status === 'error') { - throw new Error( - `ActionsClientBedrockChat: action result status is error: ${data?.message} - ${data?.serviceMessage}` - ); - } - - return { - ok: data.status === 'ok', - json: () => data.data, - } as unknown as Response; - }, - }); - } -} diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/gemini_chat.ts b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/gemini_chat.ts deleted file mode 100644 index 5c04e253cbc96..0000000000000 --- a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/gemini_chat.ts +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { - Content, - EnhancedGenerateContentResponse, - GenerateContentRequest, - GenerateContentResult, -} from '@google/generative-ai'; -import type { ActionsClient } from '@kbn/actions-plugin/server'; -import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager'; -import type { BaseMessage, UsageMetadata } from '@langchain/core/messages'; -import type { ChatGenerationChunk } from '@langchain/core/outputs'; -import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; -import type { Logger } from '@kbn/logging'; -import type { BaseChatModelParams } from '@langchain/core/language_models/chat_models'; -import { get } from 'lodash/fp'; -import type { Readable } from 'stream'; -import type { TelemetryMetadata } from '@kbn/actions-plugin/server/lib'; -import { - convertBaseMessagesToContent, - convertResponseBadFinishReasonToErrorMsg, - convertResponseContentToChatGenerationChunk, -} from '../utils/gemini'; -const DEFAULT_GEMINI_TEMPERATURE = 0; - -export interface CustomChatModelInput extends BaseChatModelParams { - actionsClient: PublicMethodsOf; - connectorId: string; - logger: Logger; - temperature?: number; - signal?: AbortSignal; - model?: string; - maxTokens?: number; - telemetryMetadata?: TelemetryMetadata; -} - -export class ActionsClientGeminiChatModel extends ChatGoogleGenerativeAI { - #actionsClient: PublicMethodsOf; - #connectorId: string; - #temperature: number; - #model?: string; - telemetryMetadata?: TelemetryMetadata; - - constructor({ actionsClient, connectorId, ...props }: CustomChatModelInput) { - super({ - ...props, - apiKey: 'asda', - maxOutputTokens: props.maxTokens ?? 2048, - }); - this.telemetryMetadata = props.telemetryMetadata; - // LangChain needs model to be defined for logging purposes - this.model = props.model ?? this.model; - // If model is not specified by consumer, the connector will defin eit so do not pass - // a LangChain default to the actionsClient - this.#model = props.model; - this.#temperature = props.temperature ?? DEFAULT_GEMINI_TEMPERATURE; - this.#actionsClient = actionsClient; - this.#connectorId = connectorId; - } - - async completionWithRetry( - request: GenerateContentRequest, - options?: this['ParsedCallOptions'] - ): Promise { - return this.caller.callWithOptions({ signal: options?.signal }, async () => { - try { - const requestBody = { - actionId: this.#connectorId, - params: { - subAction: 'invokeAIRaw', - subActionParams: { - telemetryMetadata: this.telemetryMetadata, - model: this.#model, - messages: request.contents, - tools: request.tools, - temperature: this.#temperature, - }, - }, - }; - - const actionResult = (await this.#actionsClient.execute(requestBody)) as { - status: string; - data: EnhancedGenerateContentResponse; - message?: string; - serviceMessage?: string; - }; - - if (actionResult.status === 'error') { - const error = new Error( - `ActionsClientGeminiChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}` - ); - if (actionResult?.serviceMessage) { - error.name = actionResult?.serviceMessage; - } - throw error; - } - - if (actionResult.data.candidates && actionResult.data.candidates.length > 0) { - // handle bad finish reason - const errorMessage = convertResponseBadFinishReasonToErrorMsg(actionResult.data); - if (errorMessage != null) { - throw new Error(errorMessage); - } - } - - return { - response: { - ...actionResult.data, - functionCalls: () => - actionResult.data?.candidates?.[0]?.content?.parts[0].functionCall - ? [actionResult.data?.candidates?.[0]?.content.parts[0].functionCall] - : [], - }, - }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { - // TODO: Improve error handling - if (e.message?.includes('400 Bad Request')) { - e.status = 400; - } - throw e; - } - }); - } - - async *_streamResponseChunks( - messages: BaseMessage[], - options: this['ParsedCallOptions'], - runManager?: CallbackManagerForLLMRun - ): AsyncGenerator { - const prompt = convertBaseMessagesToContent(messages, this._isMultimodalModel); - const parameters = this.invocationParams(options); - const request = { - ...parameters, - contents: prompt, - }; - - const stream = await this.caller.callWithOptions({ signal: options?.signal }, async () => { - const requestBody = { - actionId: this.#connectorId, - params: { - subAction: 'invokeStream', - subActionParams: { - model: this.#model, - messages: request.contents.reduce((acc: Content[], item) => { - if (!acc?.length) { - acc.push(item); - return acc; - } - - if (acc[acc.length - 1].role === item.role) { - acc[acc.length - 1].parts = acc[acc.length - 1].parts.concat(item.parts); - return acc; - } - - acc.push(item); - return acc; - }, []), - temperature: this.#temperature, - tools: request.tools, - telemetryMetadata: this.telemetryMetadata, - }, - }, - }; - - const actionResult = await this.#actionsClient.execute(requestBody); - - if (actionResult.status === 'error') { - const error = new Error( - `ActionsClientGeminiChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}` - ); - if (actionResult?.serviceMessage) { - error.name = actionResult?.serviceMessage; - } - throw error; - } - - const readable = get('data', actionResult) as Readable; - - if (typeof readable?.read !== 'function') { - throw new Error('Action result status is error: result is not streamable'); - } - return readable; - }); - - let usageMetadata: UsageMetadata | undefined; - let index = 0; - let partialStreamChunk = ''; - for await (const rawStreamChunk of stream) { - const streamChunk = rawStreamChunk.toString(); - - const nextChunk = `${partialStreamChunk + streamChunk}`; - - let parsedStreamChunk: EnhancedGenerateContentResponse | null = null; - try { - parsedStreamChunk = JSON.parse(nextChunk.replaceAll('data: ', '').replaceAll('\r\n', '')); - partialStreamChunk = ''; - } catch (_) { - partialStreamChunk += nextChunk; - } - - if (parsedStreamChunk !== null) { - const errorMessage = convertResponseBadFinishReasonToErrorMsg(parsedStreamChunk); - if (errorMessage != null) { - throw new Error(errorMessage); - } - const response = { - ...parsedStreamChunk, - functionCalls: () => - parsedStreamChunk?.candidates?.[0]?.content.parts[0].functionCall - ? [parsedStreamChunk.candidates?.[0]?.content.parts[0].functionCall] - : [], - }; - - if ( - 'usageMetadata' in response && - this.streamUsage !== false && - options.streamUsage !== false - ) { - const genAIUsageMetadata = response.usageMetadata as { - promptTokenCount: number; - candidatesTokenCount: number; - totalTokenCount: number; - }; - if (!usageMetadata) { - usageMetadata = { - input_tokens: genAIUsageMetadata.promptTokenCount, - output_tokens: genAIUsageMetadata.candidatesTokenCount, - total_tokens: genAIUsageMetadata.totalTokenCount, - }; - } else { - // Under the hood, LangChain combines the prompt tokens. Google returns the updated - // total each time, so we need to find the difference between the tokens. - const outputTokenDiff = - genAIUsageMetadata.candidatesTokenCount - usageMetadata.output_tokens; - usageMetadata = { - input_tokens: 0, - output_tokens: outputTokenDiff, - total_tokens: outputTokenDiff, - }; - } - } - - const chunk = convertResponseContentToChatGenerationChunk(response, { - usageMetadata, - index, - }); - index += 1; - - if (chunk) { - yield chunk; - await runManager?.handleLLMNewToken(chunk.text ?? ''); - } - } - } - } -} diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/index.ts b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/index.ts index b3c5053f11701..309778d229b97 100644 --- a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/index.ts +++ b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/index.ts @@ -5,9 +5,6 @@ * 2.0. */ -export { ActionsClientBedrockChatModel } from './bedrock_chat'; export { ActionsClientChatOpenAI } from './chat_openai'; -export { ActionsClientGeminiChatModel } from './gemini_chat'; export { ActionsClientChatVertexAI } from './chat_vertex'; export { ActionsClientLlm } from './llm'; -export { ActionsClientSimpleChatModel } from './simple_chat_model'; diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/simple_chat_model.test.ts b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/simple_chat_model.test.ts deleted file mode 100644 index fe65c6c279800..0000000000000 --- a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/simple_chat_model.test.ts +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { PassThrough } from 'stream'; -import { loggerMock } from '@kbn/logging-mocks'; -import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; - -import { ActionsClientSimpleChatModel } from './simple_chat_model'; -import { mockActionResponse } from './mocks'; -import type { BaseMessage } from '@langchain/core/messages'; -import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager'; -import { parseBedrockStream, parseBedrockStreamAsAsyncIterator } from '../utils/bedrock'; -import { parseGeminiStream, parseGeminiStreamAsAsyncIterator } from '../utils/gemini'; - -const connectorId = 'mock-connector-id'; - -const mockExecute = jest.fn(); -const actionsClient = actionsClientMock.create(); - -const mockLogger = loggerMock.create(); - -const mockStreamExecute = jest.fn().mockImplementation(() => ({ - data: new PassThrough(), - status: 'ok', -})); - -const callMessages = [ - { - lc_serializable: true, - lc_kwargs: { - content: 'Answer the following questions truthfully and as best you can.', - additional_kwargs: {}, - response_metadata: {}, - }, - lc_namespace: ['langchain_core', 'messages'], - content: 'Answer the following questions truthfully and as best you can.', - name: undefined, - additional_kwargs: {}, - response_metadata: {}, - _getType: () => 'system', - }, - { - lc_serializable: true, - lc_kwargs: { - content: 'Question: Do you know my name?\n\n', - additional_kwargs: {}, - response_metadata: {}, - }, - lc_namespace: ['langchain_core', 'messages'], - content: 'Question: Do you know my name?\n\n', - name: undefined, - additional_kwargs: {}, - response_metadata: {}, - _getType: () => 'human', - }, -] as unknown as BaseMessage[]; - -const callOptions = { - stop: ['\n'], -}; -const handleLLMNewToken = jest.fn(); -const callRunManager = { - handleLLMNewToken, -} as unknown as CallbackManagerForLLMRun; - -const defaultArgs = { - actionsClient, - connectorId, - logger: mockLogger, - streaming: false, -}; -jest.mock('../utils/bedrock'); -jest.mock('../utils/gemini'); - -describe('ActionsClientSimpleChatModel', () => { - beforeEach(() => { - jest.clearAllMocks(); - actionsClient.execute.mockImplementation( - jest.fn().mockImplementation(() => ({ - data: mockActionResponse, - status: 'ok', - })) - ); - mockExecute.mockImplementation(() => ({ - data: mockActionResponse, - status: 'ok', - })); - }); - - describe('getActionResultData', () => { - it('returns the expected data', async () => { - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel(defaultArgs); - - const result = await actionsClientSimpleChatModel._call( - callMessages, - callOptions, - callRunManager - ); - - expect(result).toEqual(mockActionResponse.message); - }); - }); - - describe('_llmType', () => { - it('returns the expected LLM type', () => { - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel(defaultArgs); - - expect(actionsClientSimpleChatModel._llmType()).toEqual('ActionsClientSimpleChatModel'); - }); - - it('returns the expected LLM type when overridden', () => { - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - llmType: 'special-llm-type', - }); - - expect(actionsClientSimpleChatModel._llmType()).toEqual('special-llm-type'); - }); - }); - - describe('_call streaming: false', () => { - it('returns the expected content when _call is invoked', async () => { - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel(defaultArgs); - - const result = await actionsClientSimpleChatModel._call( - callMessages, - callOptions, - callRunManager - ); - const subAction = actionsClient.execute.mock.calls[0][0].params.subAction; - expect(subAction).toEqual('invokeAI'); - - expect(result).toEqual(mockActionResponse.message); - }); - - it('rejects with the expected error when the action result status is error', async () => { - const hasErrorStatus = jest.fn().mockImplementation(() => { - throw Error( - 'ActionsClientSimpleChatModel: action result status is error: action-result-message - action-result-service-message' - ); - }); - - actionsClient.execute.mockRejectedValueOnce(hasErrorStatus); - - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - }); - - await expect( - actionsClientSimpleChatModel._call(callMessages, callOptions, callRunManager) - ).rejects.toThrowError( - 'ActionsClientSimpleChatModel: action result status is error: action-result-message - action-result-service-message' - ); - }); - - it('rejects with the expected error the message has invalid content', async () => { - const invalidContent = { message: 1234 }; - - actionsClient.execute.mockImplementation( - jest.fn().mockResolvedValue({ - data: invalidContent, - status: 'ok', - }) - ); - - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel(defaultArgs); - - await expect( - actionsClientSimpleChatModel._call(callMessages, callOptions, callRunManager) - ).rejects.toThrowError( - 'ActionsClientSimpleChatModel: content should be a string, but it had an unexpected type: number' - ); - }); - - it('throws multimodal error', async () => { - const invalidContent = { message: 1234 }; - - mockExecute.mockImplementation(() => ({ - data: invalidContent, - status: 'ok', - })); - - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel(defaultArgs); - - await expect( - actionsClientSimpleChatModel._call( - // @ts-ignore - [{ ...callMessages[0], content: null }], - callOptions, - callRunManager - ) - ).rejects.toThrowError('Multimodal messages are not supported'); - }); - }); - - describe('_call streaming: true', () => { - beforeEach(() => { - (parseBedrockStream as jest.Mock).mockResolvedValue(mockActionResponse.message); - (parseGeminiStream as jest.Mock).mockResolvedValue(mockActionResponse.message); - }); - it('returns the expected content when _call is invoked with streaming and llmType is Bedrock', async () => { - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'bedrock', - streaming: true, - maxTokens: 333, - }); - - const result = await actionsClientSimpleChatModel._call( - callMessages, - callOptions, - callRunManager - ); - const subAction = mockStreamExecute.mock.calls[0][0].params.subAction; - expect(subAction).toEqual('invokeStream'); - - const { messages, ...rest } = mockStreamExecute.mock.calls[0][0].params.subActionParams; - - expect(rest).toEqual({ - temperature: 0, - stopSequences: ['\n'], - maxTokens: 333, - model: undefined, - telemetryMetadata: { - aggregateBy: undefined, - pluginId: undefined, - }, - }); - - expect(result).toEqual(mockActionResponse.message); - }); - it('returns the expected content when _call is invoked with streaming and llmType is Gemini', async () => { - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'gemini', - streaming: true, - maxTokens: 333, - }); - - const result = await actionsClientSimpleChatModel._call( - callMessages, - callOptions, - callRunManager - ); - const subAction = mockStreamExecute.mock.calls[0][0].params.subAction; - expect(subAction).toEqual('invokeStream'); - const { messages, ...rest } = mockStreamExecute.mock.calls[0][0].params.subActionParams; - - expect(rest).toEqual({ - temperature: 0, - model: undefined, - telemetryMetadata: { - aggregateBy: undefined, - pluginId: undefined, - }, - }); - - expect(result).toEqual(mockActionResponse.message); - }); - it('does not call handleLLMNewToken until the final answer', async () => { - (parseBedrockStream as jest.Mock).mockImplementation((_1, _2, _3, handleToken) => { - handleToken('token1'); - handleToken('token2'); - handleToken('token3'); - handleToken('token4'); - handleToken('token5'); - handleToken(`"action":`); - handleToken(`"Final Answer"`); - handleToken(`, "action_input": "`); - handleToken('token6'); - }); - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'bedrock', - streaming: true, - }); - await actionsClientSimpleChatModel._call(callMessages, callOptions, callRunManager); - expect(handleLLMNewToken).toHaveBeenCalledTimes(1); - expect(handleLLMNewToken).toHaveBeenCalledWith('token6'); - }); - it('does not call handleLLMNewToken after the final output ends', async () => { - (parseBedrockStream as jest.Mock).mockImplementation((_1, _2, _3, handleToken) => { - handleToken('token5'); - handleToken(`"action":`); - handleToken(`"Final Answer"`); - handleToken(`, "action_input": "`); - handleToken('token6'); - handleToken('"'); - handleToken('token7'); - }); - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'bedrock', - streaming: true, - }); - await actionsClientSimpleChatModel._call(callMessages, callOptions, callRunManager); - expect(handleLLMNewToken).toHaveBeenCalledTimes(1); - expect(handleLLMNewToken).toHaveBeenCalledWith('token6'); - }); - it('extra tokens in the final answer start chunk get pushed to handleLLMNewToken', async () => { - (parseBedrockStream as jest.Mock).mockImplementation((_1, _2, _3, handleToken) => { - handleToken('token1'); - handleToken(`"action":`); - handleToken(`"Final Answer"`); - handleToken(`, "action_input": "token5 `); - handleToken('token6'); - }); - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'bedrock', - streaming: true, - }); - await actionsClientSimpleChatModel._call(callMessages, callOptions, callRunManager); - expect(handleLLMNewToken).toHaveBeenCalledTimes(2); - expect(handleLLMNewToken).toHaveBeenCalledWith('token5 '); - expect(handleLLMNewToken).toHaveBeenCalledWith('token6'); - }); - it('extra tokens in the final answer end chunk get pushed to handleLLMNewToken', async () => { - (parseBedrockStream as jest.Mock).mockImplementation((_1, _2, _3, handleToken) => { - handleToken('token5'); - handleToken(`"action":`); - handleToken(`"Final Answer"`); - handleToken(`, "action_input": "`); - handleToken('token6'); - handleToken('token7"'); - handleToken('token8'); - }); - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'bedrock', - streaming: true, - }); - await actionsClientSimpleChatModel._call(callMessages, callOptions, callRunManager); - expect(handleLLMNewToken).toHaveBeenCalledTimes(2); - expect(handleLLMNewToken).toHaveBeenCalledWith('token6'); - expect(handleLLMNewToken).toHaveBeenCalledWith('token7'); - }); - }); - - describe('*_streamResponseChunks', () => { - it('iterates over bedrock chunks', async () => { - function* mockFetchData() { - yield 'token1'; - yield 'token2'; - yield 'token3'; - } - (parseBedrockStreamAsAsyncIterator as jest.Mock).mockImplementation(mockFetchData); - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'bedrock', - streaming: true, - }); - - const gen = actionsClientSimpleChatModel._streamResponseChunks( - callMessages, - callOptions, - callRunManager - ); - - const chunks = []; - - for await (const chunk of gen) { - chunks.push(chunk); - } - - expect(chunks.map((c) => c.text)).toEqual(['token1', 'token2', 'token3']); - expect(handleLLMNewToken).toHaveBeenCalledTimes(3); - expect(handleLLMNewToken).toHaveBeenCalledWith('token1'); - expect(handleLLMNewToken).toHaveBeenCalledWith('token2'); - expect(handleLLMNewToken).toHaveBeenCalledWith('token3'); - }); - it('iterates over gemini chunks', async () => { - function* mockFetchData() { - yield 'token1'; - yield 'token2'; - yield 'token3'; - } - (parseGeminiStreamAsAsyncIterator as jest.Mock).mockImplementation(mockFetchData); - actionsClient.execute.mockImplementationOnce(mockStreamExecute); - - const actionsClientSimpleChatModel = new ActionsClientSimpleChatModel({ - ...defaultArgs, - actionsClient, - llmType: 'gemini', - streaming: true, - }); - - const gen = actionsClientSimpleChatModel._streamResponseChunks( - callMessages, - callOptions, - callRunManager - ); - - const chunks = []; - - for await (const chunk of gen) { - chunks.push(chunk); - } - - expect(chunks.map((c) => c.text)).toEqual(['token1', 'token2', 'token3']); - expect(handleLLMNewToken).toHaveBeenCalledTimes(3); - expect(handleLLMNewToken).toHaveBeenCalledWith('token1'); - expect(handleLLMNewToken).toHaveBeenCalledWith('token2'); - expect(handleLLMNewToken).toHaveBeenCalledWith('token3'); - }); - }); -}); diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/simple_chat_model.ts b/x-pack/platform/packages/shared/kbn-langchain/server/language_models/simple_chat_model.ts deleted file mode 100644 index 84ed92deec075..0000000000000 --- a/x-pack/platform/packages/shared/kbn-langchain/server/language_models/simple_chat_model.ts +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Readable } from 'stream'; -import { - SimpleChatModel, - type BaseChatModelParams, -} from '@langchain/core/language_models/chat_models'; -import { AIMessageChunk, type BaseMessage } from '@langchain/core/messages'; -import type { ActionsClient } from '@kbn/actions-plugin/server'; -import type { Logger } from '@kbn/logging'; -import { v4 as uuidv4 } from 'uuid'; -import { get } from 'lodash/fp'; -import { ChatGenerationChunk } from '@langchain/core/outputs'; -import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager'; -import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { TelemetryMetadata } from '@kbn/actions-plugin/server/lib'; -import { parseGeminiStreamAsAsyncIterator, parseGeminiStream } from '../utils/gemini'; -import { parseBedrockStreamAsAsyncIterator, parseBedrockStream } from '../utils/bedrock'; -import { getDefaultArguments } from './constants'; - -export const getMessageContentAndRole = (prompt: string, role = 'user') => ({ - content: prompt, - role: role === 'human' ? 'user' : role, -}); - -export interface CustomChatModelInput extends BaseChatModelParams { - actionsClient: PublicMethodsOf; - connectorId: string; - logger: Logger; - llmType?: string; - signal?: AbortSignal; - model?: string; - temperature?: number; - streaming: boolean; - maxTokens?: number; - telemetryMetadata?: TelemetryMetadata; -} - -function _formatMessages(messages: BaseMessage[]) { - if (!messages.length) { - throw new Error('No messages provided.'); - } - return messages.map((message) => { - if (typeof message.content !== 'string') { - throw new Error('Multimodal messages are not supported.'); - } - return getMessageContentAndRole(message.content, message._getType()); - }); -} - -export class ActionsClientSimpleChatModel extends SimpleChatModel { - #actionsClient: PublicMethodsOf; - #connectorId: string; - #logger: Logger; - #traceId: string; - #signal?: AbortSignal; - #maxTokens?: number; - llmType: string; - streaming: boolean; - model?: string; - temperature?: number; - telemetryMetadata?: TelemetryMetadata; - - constructor({ - actionsClient, - connectorId, - llmType, - logger, - model, - temperature, - signal, - streaming, - maxTokens, - telemetryMetadata, - }: CustomChatModelInput) { - super({}); - - this.#actionsClient = actionsClient; - this.#connectorId = connectorId; - this.#traceId = uuidv4(); - this.#logger = logger; - this.#signal = signal; - this.#maxTokens = maxTokens; - this.llmType = llmType ?? 'ActionsClientSimpleChatModel'; - this.model = model; - this.temperature = temperature; - this.streaming = streaming; - this.telemetryMetadata = telemetryMetadata; - } - - _llmType() { - return this.llmType; - } - - // Model type needs to be `base_chat_model` to work with LangChain OpenAI Tools - // We may want to make this configurable (ala _llmType) if different agents end up requiring different model types - // See: https://github.com/langchain-ai/langchainjs/blob/fb699647a310c620140842776f4a7432c53e02fa/langchain/src/agents/openai/index.ts#L185 - _modelType() { - return 'base_chat_model'; - } - - async _call( - messages: BaseMessage[], - options: this['ParsedCallOptions'], - runManager?: CallbackManagerForLLMRun - ): Promise { - const formattedMessages = _formatMessages(messages); - this.#logger.debug( - () => - `ActionsClientSimpleChatModel#_call\ntraceId: ${ - this.#traceId - }\nassistantMessage:\n${JSON.stringify(formattedMessages)} ` - ); - // create a new connector request body with the assistant message: - const requestBody = { - actionId: this.#connectorId, - params: { - subAction: this.streaming ? 'invokeStream' : 'invokeAI', - subActionParams: { - model: this.model, - messages: formattedMessages, - telemetryMetadata: { - pluginId: this.telemetryMetadata?.pluginId, - aggregateBy: this.telemetryMetadata?.aggregateBy, - }, - ...getDefaultArguments(this.llmType, this.temperature, options.stop, this.#maxTokens), - }, - }, - }; - - const actionResult = await this.#actionsClient.execute(requestBody); - - if (actionResult.status === 'error') { - const error = new Error( - `ActionsClientSimpleChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}` - ); - if (actionResult?.serviceMessage) { - error.name = actionResult?.serviceMessage; - } - throw error; - } - - if (!this.streaming) { - const content = get('data.message', actionResult); - - if (typeof content !== 'string') { - throw new Error( - `ActionsClientSimpleChatModel: content should be a string, but it had an unexpected type: ${typeof content}` - ); - } - - return content; // per the contact of _call, return a string - } - - const readable = get('data', actionResult) as Readable; - - if (typeof readable?.read !== 'function') { - throw new Error('Action result status is error: result is not streamable'); - } - - let currentOutput = ''; - let finalOutputIndex = -1; - const finalOutputStartToken = '"action":"FinalAnswer","action_input":"'; - let streamingFinished = false; - const finalOutputStopRegex = /(? { - if (finalOutputIndex === -1) { - currentOutput += token; - // Remove whitespace to simplify parsing - const noWhitespaceOutput = currentOutput.replace(/\s/g, ''); - if (noWhitespaceOutput.includes(finalOutputStartToken)) { - const nonStrippedToken = '"action_input": "'; - finalOutputIndex = currentOutput.indexOf(nonStrippedToken); - const contentStartIndex = finalOutputIndex + nonStrippedToken.length; - const extraOutput = currentOutput.substring(contentStartIndex); - if (extraOutput.length > 0) { - await runManager?.handleLLMNewToken(extraOutput); - } - } - } else if (!streamingFinished) { - const finalOutputEndIndex = token.search(finalOutputStopRegex); - if (finalOutputEndIndex !== -1) { - streamingFinished = true; - const extraOutput = token.substring(0, finalOutputEndIndex); - streamingFinished = true; - if (extraOutput.length > 0) { - await runManager?.handleLLMNewToken(extraOutput); - } - } else { - await runManager?.handleLLMNewToken(token); - } - } - }; - const streamParser = this.llmType === 'bedrock' ? parseBedrockStream : parseGeminiStream; - - const parsed = await streamParser(readable, this.#logger, this.#signal, handleLLMNewToken); - - return parsed; // per the contact of _call, return a string - } - - async *_streamResponseChunks( - messages: BaseMessage[], - options: this['ParsedCallOptions'], - runManager?: CallbackManagerForLLMRun | undefined - ): AsyncGenerator { - const formattedMessages = _formatMessages(messages); - this.#logger.debug( - () => - `ActionsClientSimpleChatModel#stream\ntraceId: ${ - this.#traceId - }\nassistantMessage:\n${JSON.stringify(formattedMessages)} ` - ); - // create a new connector request body with the assistant message: - const requestBody = { - actionId: this.#connectorId, - params: { - subAction: 'invokeStream', - subActionParams: { - model: this.model, - messages: formattedMessages, - telemetryMetadata: { - pluginId: this.telemetryMetadata?.pluginId, - aggregateBy: this.telemetryMetadata?.aggregateBy, - }, - ...getDefaultArguments(this.llmType, this.temperature, options.stop, this.#maxTokens), - }, - }, - }; - const actionResult = await this.#actionsClient.execute(requestBody); - - if (actionResult.status === 'error') { - const error = new Error( - `ActionsClientSimpleChatModel: action result status is error: ${actionResult?.message} - ${actionResult?.serviceMessage}` - ); - if (actionResult?.serviceMessage) { - error.name = actionResult?.serviceMessage; - } - throw error; - } - - const readable = get('data', actionResult) as Readable; - - if (typeof readable?.read !== 'function') { - throw new Error('Action result status is error: result is not streamable'); - } - - const streamParser = - this.llmType === 'bedrock' - ? parseBedrockStreamAsAsyncIterator - : parseGeminiStreamAsAsyncIterator; - for await (const token of streamParser(readable, this.#logger, this.#signal)) { - yield new ChatGenerationChunk({ - message: new AIMessageChunk({ content: token }), - text: token, - }); - await runManager?.handleLLMNewToken(token); - } - } -} diff --git a/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts index a602b0b5ad64a..cc17a8d1561c2 100644 --- a/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts +++ b/x-pack/platform/packages/shared/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts @@ -96,7 +96,7 @@ export class TelemetryTracer extends BaseTracer implements LangChainTracerFields : {}; const telemetryValue = { ...telemetryParams, - durationMs: (run.end_time ?? 0) - (run.start_time ?? 0), + durationMs: (Number(run.end_time) || 0) - (Number(run.start_time) || 0), toolsInvoked, ...(telemetryParams.actionTypeId === '.gen-ai' ? { isOssModel: run.inputs.isOssModel } diff --git a/x-pack/platform/packages/shared/kbn-langgraph-checkpoint-saver/server/elastic-search-checkpoint-saver/index.ts b/x-pack/platform/packages/shared/kbn-langgraph-checkpoint-saver/server/elastic-search-checkpoint-saver/index.ts index 2b1e4b82d2ebf..5f4e5e0499a3c 100644 --- a/x-pack/platform/packages/shared/kbn-langgraph-checkpoint-saver/server/elastic-search-checkpoint-saver/index.ts +++ b/x-pack/platform/packages/shared/kbn-langgraph-checkpoint-saver/server/elastic-search-checkpoint-saver/index.ts @@ -343,8 +343,8 @@ export class ElasticSearchSaver extends BaseCheckpointSaver { ); } - const [checkpointType, serializedCheckpoint] = this.serde.dumpsTyped(checkpoint); - const [metadataType, serializedMetadata] = this.serde.dumpsTyped(metadata); + const [checkpointType, serializedCheckpoint] = await this.serde.dumpsTyped(checkpoint); + const [metadataType, serializedMetadata] = await this.serde.dumpsTyped(metadata); if (checkpointType !== metadataType) { throw new Error('Mismatched checkpoint and metadata types.'); } @@ -396,36 +396,40 @@ export class ElasticSearchSaver extends BaseCheckpointSaver { ); } - const operations = writes.flatMap((write, idx) => { - const [channel, value] = write; - - const compositeId = `thread_id:${threadId}|checkpoint_ns:${checkpointNs}|checkpoint_id:${checkpointId}|task_id:${taskId}|idx:${idx}`; - const [type, serializedValue] = this.serde.dumpsTyped(value); - - const doc: WritesDocument = { - '@timestamp': new Date().toISOString(), - thread_id: threadId, - checkpoint_ns: checkpointNs, - checkpoint_id: checkpointId, - task_id: taskId, - idx, - channel, - value: Buffer.from(serializedValue).toString('base64'), - type, - }; - - this.logger.debug(`Indexing write operation for checkpoint ${checkpointId}`); - - return [ - { - update: { - _index: this.checkpointWritesIndex, - _id: compositeId, - }, - }, - { doc, doc_as_upsert: true }, - ]; - }); + const operations = ( + await Promise.all( + writes.map(async (write, idx) => { + const [channel, value] = write; + + const compositeId = `thread_id:${threadId}|checkpoint_ns:${checkpointNs}|checkpoint_id:${checkpointId}|task_id:${taskId}|idx:${idx}`; + const [type, serializedValue] = await this.serde.dumpsTyped(value); + + const doc: WritesDocument = { + '@timestamp': new Date().toISOString(), + thread_id: threadId, + checkpoint_ns: checkpointNs, + checkpoint_id: checkpointId, + task_id: taskId, + idx, + channel, + value: Buffer.from(serializedValue).toString('base64'), + type, + }; + + this.logger.debug(`Indexing write operation for checkpoint ${checkpointId}`); + + return [ + { + update: { + _index: this.checkpointWritesIndex, + _id: compositeId, + }, + }, + { doc, doc_as_upsert: true }, + ]; + }) + ) + ).flat(); const result = await this.client.bulk({ operations, diff --git a/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_logs_routes.ts b/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_logs_routes.ts index 01ab1fc3837b3..e4040ad9a0b7d 100644 --- a/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_logs_routes.ts +++ b/x-pack/platform/plugins/shared/automatic_import/server/routes/analyze_logs_routes.ts @@ -100,7 +100,8 @@ export function registerAnalyzeLogsRoutes(router: IRouter { properties: { foo: { description: 'foo', - enum: undefined, + enum: [], + format: 'enum', type: 'string', }, }, diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts index e66e9e2277f35..ddbede3d73780 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts @@ -110,7 +110,7 @@ function toolSchemaToGemini({ schema }: { schema: ToolSchema }): Gemini.Function return { type: Gemini.SchemaType.ARRAY, description: def.description, - items: convertSchemaType({ def: def.items }) as Gemini.FunctionDeclarationSchema, + items: convertSchemaType({ def: def.items }), }; case 'object': return { @@ -131,20 +131,19 @@ function toolSchemaToGemini({ schema }: { schema: ToolSchema }): Gemini.Function case 'string': return { type: Gemini.SchemaType.STRING, + format: 'enum', description: def.description, - enum: def.enum ? (def.enum as string[]) : def.const ? [def.const] : undefined, + enum: def.enum ? (def.enum as string[]) : def.const ? [def.const] : [], }; case 'boolean': return { type: Gemini.SchemaType.BOOLEAN, description: def.description, - enum: def.enum ? (def.enum as string[]) : def.const ? [def.const] : undefined, }; case 'number': return { type: Gemini.SchemaType.NUMBER, description: def.description, - enum: def.enum ? (def.enum as string[]) : def.const ? [def.const] : undefined, }; } }; diff --git a/x-pack/solutions/search/plugins/search_playground/server/lib/get_chat_params.test.ts b/x-pack/solutions/search/plugins/search_playground/server/lib/get_chat_params.test.ts index a2b61b199a490..bf674792670f5 100644 --- a/x-pack/solutions/search/plugins/search_playground/server/lib/get_chat_params.test.ts +++ b/x-pack/solutions/search/plugins/search_playground/server/lib/get_chat_params.test.ts @@ -25,7 +25,6 @@ jest.mock('@kbn/langchain/server', () => { return { ...original, ActionsClientChatOpenAI: jest.fn(), - ActionsClientSimpleChatModel: jest.fn(), }; }); diff --git a/x-pack/solutions/security/plugins/elastic_assistant/scripts/draw_graph_script.ts b/x-pack/solutions/security/plugins/elastic_assistant/scripts/draw_graph_script.ts index d0b43203746ea..9d32160f8b295 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/scripts/draw_graph_script.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/scripts/draw_graph_script.ts @@ -11,7 +11,6 @@ import fs from 'fs/promises'; import path from 'path'; import type { ActionsClientChatOpenAI, - ActionsClientSimpleChatModel, ActionsClientLlm, } from '@kbn/langchain/server/language_models'; import type { Logger } from '@kbn/logging'; @@ -50,7 +49,7 @@ interface Drawable { const mockLlm = new FakeLLM({ response: JSON.stringify({}, null, 2), -}) as unknown as ActionsClientChatOpenAI | ActionsClientSimpleChatModel; +}) as unknown as ActionsClientChatOpenAI; class FakeChatModelWithBindTools extends FakeChatModel { bindTools = () => this; 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 8d1858ac81eaf..8d3820a42c28d 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 @@ -527,7 +527,7 @@ export const invokeDefendInsightsGraph = async ({ runName: DEFEND_INSIGHTS_GRAPH_RUN_NAME, tags, } - )) as DefendInsightsGraphState; + )) as unknown as DefendInsightsGraphState; const { insights, anonymizedDocuments: anonymizedEvents, diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts index 83821575aae07..2fb8f9784bbee 100755 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts @@ -44,7 +44,6 @@ import type { import type { ActionsClientChatVertexAI, ActionsClientChatOpenAI, - ActionsClientGeminiChatModel, ActionsClientChatBedrockConverse, ActionsClientLlm, } from '@kbn/langchain/server'; @@ -271,7 +270,6 @@ export interface AssistantTool { export type AssistantToolLlm = | ActionsClientChatBedrockConverse | ActionsClientChatOpenAI - | ActionsClientGeminiChatModel | ActionsClientChatVertexAI | InferenceChatModel; diff --git a/yarn.lock b/yarn.lock index 2f6d29d77f9b9..5212c467ed693 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9222,22 +9222,22 @@ zod "^3.22.3" zod-to-json-schema "^3.22.5" -"@langchain/core@>0.1.0 <0.3.0", "@langchain/core@^0.3.57": - version "0.3.57" - resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.57.tgz#3e9d9414046873405f48c761fe632bd757250a91" - integrity sha512-jz28qCTKJmi47b6jqhQ6vYRTG5jRpqhtPQjriRTB5wR8mgvzo6xKs0fG/kExS3ZvM79ytD1npBvgf8i19xOo9Q== +"@langchain/core@>0.1.0 <0.3.0", "@langchain/core@^0.3.80": + version "0.3.80" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.80.tgz#c494a6944e53ab28bf32dc531e257b17cfc8f797" + integrity sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA== dependencies: "@cfworker/json-schema" "^4.0.2" ansi-styles "^5.0.0" camelcase "6" decamelize "1.2.0" js-tiktoken "^1.0.12" - langsmith "^0.3.29" + langsmith "^0.3.67" mustache "^4.2.0" p-queue "^6.6.2" p-retry "4" uuid "^10.0.0" - zod "^3.22.4" + zod "^3.25.32" zod-to-json-schema "^3.22.3" "@langchain/google-common@^0.1.8", "@langchain/google-common@~0.1.8": @@ -9298,7 +9298,16 @@ uuid "^10.0.0" zod "^3.23.8" -"@langchain/openai@>=0.1.0 <0.5.0", "@langchain/openai@>=0.2.0 <0.6.0", "@langchain/openai@^0.4.4": +"@langchain/openai@>=0.1.0 <0.7.0": + version "0.6.14" + resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.6.14.tgz#09f7370870804a9f0558a063af1957babfd593ad" + integrity sha512-SM/xJOFDxT9NN/07fvhNB5dgAsIOQaLhmANxrRlSQ7Qs1zImMrzOvq+/5JP/ifpC/YxcgEnt4dblKVqvNU/C5A== + dependencies: + js-tiktoken "^1.0.12" + openai "5.12.2" + zod "^3.25.32" + +"@langchain/openai@>=0.2.0 <0.6.0", "@langchain/openai@^0.4.4": version "0.4.4" resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.4.4.tgz#1832420495c53c28aa4e6515583bad8f0b83a637" integrity sha512-UZybJeMd8+UX7Kn47kuFYfqKdBCeBUWNqDtmAr6ZUIMMnlsNIb6MkrEEhGgAEjGCpdT4CU8U/DyyddTz+JayOQ== @@ -24672,28 +24681,27 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -"langchain@>=0.2.3 <0.3.0 || >=0.3.4 <0.4.0", langchain@^0.3.15: - version "0.3.15" - resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.3.15.tgz#c6c8b17bf20c870795e31515f48abde7d13ccc9d" - integrity sha512-+DQ4I2iy4b5sErkxo6jAkgmumvhgqLwLB2fmiGl3yDt8+VVZdB1MUULZMzf+6ubarNc7Mwn/sxHUqK4GhEndhg== +"langchain@>=0.2.3 <0.3.0 || >=0.3.4 <0.4.0", langchain@^0.3.15, langchain@^0.3.37: + version "0.3.37" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.3.37.tgz#6931ee5af763a6df35c0ac467eab028ba0ad17de" + integrity sha512-1jPsZ6xsxkcQPUvqRjvfuOLwZLLyt49hzcOK7OYAJovIkkOxd5gzK4Yw6giPUQ8g4XHyvULNlWBz+subdkcokw== dependencies: - "@langchain/openai" ">=0.1.0 <0.5.0" + "@langchain/openai" ">=0.1.0 <0.7.0" "@langchain/textsplitters" ">=0.0.0 <0.2.0" js-tiktoken "^1.0.12" js-yaml "^4.1.0" jsonpointer "^5.0.1" - langsmith ">=0.2.8 <0.4.0" + langsmith "^0.3.67" openapi-types "^12.1.3" p-retry "4" uuid "^10.0.0" yaml "^2.2.1" - zod "^3.22.4" - zod-to-json-schema "^3.22.3" + zod "^3.25.32" -"langsmith@>=0.2.8 <0.4.0", langsmith@^0.3.29, langsmith@^0.3.7: - version "0.3.29" - resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.29.tgz#75b8df90b8a724f320ff0cdc784768117e17a922" - integrity sha512-JPF2B339qpYy9FyuY4Yz1aWYtgPlFc/a+VTj3L/JcFLHCiMP7+Ig8I9jO+o1QwVa+JU3iugL1RS0wwc+Glw0zA== +langsmith@^0.3.29, langsmith@^0.3.67, langsmith@^0.3.7: + version "0.3.72" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.72.tgz#82e1dc8e23d7e1101e9e26353b18fb3170884f29" + integrity sha512-XjTonMq2fIebzV0BRlPx8mi+Ih/NsQT6W484hrW/pJYuq0aT5kpLtzQthVVmsXH8ZYYkgkbQ5Gh5Mz1qoCrAwg== dependencies: "@types/uuid" "^10.0.0" chalk "^4.1.2" @@ -27132,6 +27140,11 @@ open@^8.0.4, open@^8.4.0, open@~8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +openai@5.12.2: + version "5.12.2" + resolved "https://registry.yarnpkg.com/openai/-/openai-5.12.2.tgz#512ab6b80eb8414837436e208f1b951442b97761" + integrity sha512-xqzHHQch5Tws5PcKR2xsZGX9xtch+JQFz5zb14dGqlshmmDAFBFEWmeIpf7wVqWV+w7Emj7jRgkNJakyKE0tYQ== + openai@^4.72.0, openai@^4.77.0, openai@^4.86.1: version "4.104.0" resolved "https://registry.yarnpkg.com/openai/-/openai-4.104.0.tgz#c489765dc051b95019845dab64b0e5207cae4d30" @@ -34919,7 +34932,7 @@ zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.4, zod-to-json-schema@^3.22 resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz#df504c957c4fb0feff467c74d03e6aab0b013e1c" integrity sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ== -zod@3.25.76, zod@^3.22.3, zod@^3.22.4, zod@^3.23.8, zod@^3.24.1, zod@^3.24.2, "zod@^3.25 || ^4.0", zod@^3.25.76: +zod@3.25.76, zod@^3.22.3, zod@^3.22.4, zod@^3.23.8, zod@^3.24.1, zod@^3.24.2, "zod@^3.25 || ^4.0", zod@^3.25.32, zod@^3.25.76: version "3.25.76" resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==