diff --git a/public/plugin.tsx b/public/plugin.tsx index 38a38795..7aa4424e 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -30,7 +30,7 @@ interface PublicConfig { chat: { // whether chat feature is enabled, UI should hide if false enabled: boolean; - rootAgentId?: string; + rootAgentName?: string; }; } @@ -75,7 +75,7 @@ export class AssistantPlugin const checkAccess = (account: Awaited>) => account.data.roles.some((role) => ['all_access', 'assistant_user'].includes(role)); - if (this.config.chat.enabled && this.config.chat.rootAgentId) { + if (this.config.chat.enabled) { core.getStartServices().then(async ([coreStart, startDeps]) => { const CoreContext = createOpenSearchDashboardsReactContext({ ...coreStart, diff --git a/server/index.ts b/server/index.ts index 6b5e9028..9da72ec3 100644 --- a/server/index.ts +++ b/server/index.ts @@ -17,7 +17,7 @@ const assistantConfig = { schema: schema.object({ chat: schema.object({ enabled: schema.boolean({ defaultValue: false }), - rootAgentId: schema.maybe(schema.string()), + rootAgentName: schema.maybe(schema.string()), }), }), }; diff --git a/server/plugin.ts b/server/plugin.ts index bbca5bff..0496d7d5 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -16,7 +16,7 @@ import { setupRoutes } from './routes/index'; import { AssistantPluginSetup, AssistantPluginStart, MessageParser } from './types'; import { BasicInputOutputParser } from './parsers/basic_input_output_parser'; import { VisualizationCardParser } from './parsers/visualization_card_parser'; -import { AgentIdNotFoundError } from './routes/chat_routes'; +import { AgentNameNotFoundError } from './routes/chat_routes'; export class AssistantPlugin implements Plugin { private readonly logger: Logger; @@ -34,11 +34,11 @@ export class AssistantPlugin implements Plugin ({ diff --git a/server/routes/chat_routes.ts b/server/routes/chat_routes.ts index ead1f995..70551f4c 100644 --- a/server/routes/chat_routes.ts +++ b/server/routes/chat_routes.ts @@ -37,8 +37,8 @@ const llmRequestRoute = { }; export type LLMRequestSchema = TypeOf; -export const AgentIdNotFoundError = - 'rootAgentId is required, please specify one in opensearch_dashboards.yml'; +export const AgentNameNotFoundError = + 'rootAgentName is required, please specify one in opensearch_dashboards.yml'; const getSessionRoute = { path: `${ASSISTANT_API.SESSION}/{sessionId}`, @@ -135,7 +135,8 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions) context.core.opensearch.client.asCurrentUser, routeOptions.messageParsers ); - const createChatService = () => new OllyChatService(); + const createChatService = (context: RequestHandlerContext, rootAgentName: string) => + new OllyChatService(context, rootAgentName); router.post( llmRequestRoute, @@ -144,13 +145,13 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions) request, response ): Promise> => { - if (!routeOptions.rootAgentId) { - context.assistant_plugin.logger.error(AgentIdNotFoundError); - return response.custom({ statusCode: 400, body: AgentIdNotFoundError }); + if (!routeOptions.rootAgentName) { + context.assistant_plugin.logger.error(AgentNameNotFoundError); + return response.custom({ statusCode: 400, body: AgentNameNotFoundError }); } const { messages = [], input, sessionId: sessionIdInRequestBody } = request.body; const storageService = createStorageService(context); - const chatService = createChatService(); + const chatService = createChatService(context, routeOptions.rootAgentName); let outputs: Awaited> | undefined; @@ -158,15 +159,11 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions) * Get final answer from Agent framework */ try { - outputs = await chatService.requestLLM( - { - messages, - input, - sessionId: sessionIdInRequestBody, - rootAgentId: routeOptions.rootAgentId, - }, - context - ); + outputs = await chatService.requestLLM({ + messages, + input, + sessionId: sessionIdInRequestBody, + }); } catch (error) { context.assistant_plugin.logger.error(error); const sessionId = outputs?.memoryId || sessionIdInRequestBody; @@ -329,8 +326,7 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions) request, response ): Promise> => { - const chatService = createChatService(); - + const chatService = createChatService(context, ''); try { chatService.abortAgentExecution(request.body.sessionId); context.assistant_plugin.logger.info(`Abort agent execution: ${request.body.sessionId}`); @@ -349,13 +345,13 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions) request, response ): Promise> => { - if (!routeOptions.rootAgentId) { - context.assistant_plugin.logger.error(AgentIdNotFoundError); - return response.custom({ statusCode: 400, body: AgentIdNotFoundError }); + if (!routeOptions.rootAgentName) { + context.assistant_plugin.logger.error(AgentNameNotFoundError); + return response.custom({ statusCode: 400, body: AgentNameNotFoundError }); } const { sessionId, interactionId } = request.body; const storageService = createStorageService(context); - const chatService = createChatService(); + const chatService = createChatService(context, routeOptions.rootAgentName); let outputs: Awaited> | undefined; @@ -363,10 +359,7 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions) * Get final answer from Agent framework */ try { - outputs = await chatService.regenerate( - { sessionId, rootAgentId: routeOptions.rootAgentId, interactionId }, - context - ); + outputs = await chatService.regenerate({ sessionId, interactionId }); } catch (error) { context.assistant_plugin.logger.error(error); } diff --git a/server/routes/regenerate.test.ts b/server/routes/regenerate.test.ts index 5dc6a491..54e65d88 100644 --- a/server/routes/regenerate.test.ts +++ b/server/routes/regenerate.test.ts @@ -14,12 +14,12 @@ import { } from '../services/storage/agent_framework_storage_service.mock'; import { httpServerMock } from '../../../../src/core/server/http/http_server.mocks'; import { loggerMock } from '../../../../src/core/server/logging/logger.mock'; -import { registerChatRoutes, RegenerateSchema, AgentIdNotFoundError } from './chat_routes'; +import { registerChatRoutes, RegenerateSchema, AgentNameNotFoundError } from './chat_routes'; import { ASSISTANT_API } from '../../common/constants/llm'; const mockedLogger = loggerMock.create(); -describe('regenerate route when rootAgentId is provided', () => { +describe('regenerate route when rootAgentName is provided', () => { const router = new Router( '', mockedLogger, @@ -31,7 +31,7 @@ describe('regenerate route when rootAgentId is provided', () => { ); registerChatRoutes(router, { messageParsers: [], - rootAgentId: 'foo', + rootAgentName: 'foo', }); const regenerateRequest = (payload: RegenerateSchema) => triggerHandler(router, { @@ -169,7 +169,7 @@ describe('regenerate route when rootAgentId is provided', () => { }); }); -describe('regenerate route when rootAgentId is not provided', () => { +describe('regenerate route when rootAgentName is not provided', () => { const router = new Router( '', mockedLogger, @@ -200,13 +200,13 @@ describe('regenerate route when rootAgentId is not provided', () => { sessionId: 'foo', })) as Boom; expect(mockedLogger.error).toBeCalledTimes(1); - expect(mockedLogger.error).toBeCalledWith(AgentIdNotFoundError); + expect(mockedLogger.error).toBeCalledWith(AgentNameNotFoundError); expect(result.output).toMatchInlineSnapshot(` Object { "headers": Object {}, "payload": Object { "error": "Bad Request", - "message": "rootAgentId is required, please specify one in opensearch_dashboards.yml", + "message": "rootAgentName is required, please specify one in opensearch_dashboards.yml", "statusCode": 400, }, "statusCode": 400, diff --git a/server/routes/send_message.test.ts b/server/routes/send_message.test.ts index 63b6b067..00c4e575 100644 --- a/server/routes/send_message.test.ts +++ b/server/routes/send_message.test.ts @@ -14,12 +14,12 @@ import { } from '../services/storage/agent_framework_storage_service.mock'; import { httpServerMock } from '../../../../src/core/server/http/http_server.mocks'; import { loggerMock } from '../../../../src/core/server/logging/logger.mock'; -import { registerChatRoutes, LLMRequestSchema, AgentIdNotFoundError } from './chat_routes'; +import { registerChatRoutes, LLMRequestSchema, AgentNameNotFoundError } from './chat_routes'; import { ASSISTANT_API } from '../../common/constants/llm'; const mockedLogger = loggerMock.create(); -describe('send_message route when rootAgentId is provided', () => { +describe('send_message route when rootAgentName is provided', () => { const router = new Router( '', mockedLogger, @@ -31,7 +31,7 @@ describe('send_message route when rootAgentId is provided', () => { ); registerChatRoutes(router, { messageParsers: [], - rootAgentId: 'foo', + rootAgentName: 'foo', }); const sendMessageRequest = (payload: LLMRequestSchema) => triggerHandler(router, { @@ -265,7 +265,7 @@ describe('send_message route when rootAgentId is provided', () => { }); }); -describe('send_message route when rootAgentId is not provided', () => { +describe('send_message route when rootAgentName is not provided', () => { const router = new Router( '', mockedLogger, @@ -303,13 +303,13 @@ describe('send_message route when rootAgentId is not provided', () => { sessionId: 'foo', })) as Boom; expect(mockedLogger.error).toBeCalledTimes(1); - expect(mockedLogger.error).toBeCalledWith(AgentIdNotFoundError); + expect(mockedLogger.error).toBeCalledWith(AgentNameNotFoundError); expect(result.output).toMatchInlineSnapshot(` Object { "headers": Object {}, "payload": Object { "error": "Bad Request", - "message": "rootAgentId is required, please specify one in opensearch_dashboards.yml", + "message": "rootAgentName is required, please specify one in opensearch_dashboards.yml", "statusCode": 400, }, "statusCode": 400, diff --git a/server/services/chat/olly_chat_service.test.ts b/server/services/chat/olly_chat_service.test.ts index ef76f391..1876b200 100644 --- a/server/services/chat/olly_chat_service.test.ts +++ b/server/services/chat/olly_chat_service.test.ts @@ -7,9 +7,10 @@ import { OllyChatService } from './olly_chat_service'; import { CoreRouteHandlerContext } from '../../../../../src/core/server/core_route_handler_context'; import { coreMock, httpServerMock } from '../../../../../src/core/server/mocks'; import { loggerMock } from '../../../../../src/core/server/logging/logger.mock'; +import { ResponseError } from '@opensearch-project/opensearch/lib/errors'; +import { ApiResponse } from '@opensearch-project/opensearch'; describe('OllyChatService', () => { - const ollyChatService = new OllyChatService(); const coreContext = new CoreRouteHandlerContext( coreMock.createInternalStart(), httpServerMock.createOpenSearchDashboardsRequest() @@ -22,41 +23,74 @@ describe('OllyChatService', () => { logger: loggerMock.create(), }, }; - beforeEach(() => { + const ollyChatService: OllyChatService = new OllyChatService(contextMock, 'test'); + beforeEach(async () => { mockedTransport.mockClear(); + ollyChatService.resetRootAgentId(); }); + it('requestLLM should invoke client call with correct params', async () => { - mockedTransport.mockImplementationOnce(() => { - return { - body: { - inference_results: [ - { - output: [ + mockedTransport.mockImplementation((args) => { + if (args.path === '/_plugins/_ml/agents/_search') { + return { + body: { + hits: { + total: { + value: 1, + }, + hits: [ { - name: 'memory_id', - result: 'foo', + _index: '.plugins-ml-agent', + _id: 'rootAgentId', }, ], }, - ], - }, - }; + }, + }; + } else { + return { + body: { + inference_results: [ + { + output: [ + { + name: 'memory_id', + result: 'foo', + }, + ], + }, + ], + }, + }; + } }); - const result = await ollyChatService.requestLLM( - { - messages: [], - input: { - type: 'input', - contentType: 'text', - content: 'content', - }, - sessionId: 'sessionId', - rootAgentId: 'rootAgentId', + const result = await ollyChatService.requestLLM({ + messages: [], + input: { + type: 'input', + contentType: 'text', + content: 'content', }, - contextMock - ); + sessionId: 'sessionId', + }); expect(mockedTransport.mock.calls).toMatchInlineSnapshot(` Array [ + Array [ + Object { + "body": Object { + "query": Object { + "term": Object { + "name.keyword": "test", + }, + }, + "sort": Object { + "created_time": "desc", + }, + }, + "method": "GET", + "path": "/_plugins/_ml/agents/_search", + }, + ], Array [ Object { "body": Object { @@ -86,53 +120,97 @@ describe('OllyChatService', () => { }); it('requestLLM should throw error when transport.request throws error', async () => { - mockedTransport.mockImplementationOnce(() => { - throw new Error('error'); - }); + mockedTransport + .mockImplementationOnce(() => { + return { + body: { + hits: { + total: { + value: 1, + }, + hits: [ + { + _index: '.plugins-ml-agent', + _id: 'rootAgentId', + }, + ], + }, + }, + }; + }) + .mockImplementationOnce(() => { + throw new Error('error'); + }); expect( - ollyChatService.requestLLM( - { - messages: [], - input: { - type: 'input', - contentType: 'text', - content: 'content', - }, - sessionId: '', - rootAgentId: 'rootAgentId', + ollyChatService.requestLLM({ + messages: [], + input: { + type: 'input', + contentType: 'text', + content: 'content', }, - contextMock - ) + sessionId: '', + }) ).rejects.toMatchInlineSnapshot(`[Error: error]`); }); it('regenerate should invoke client call with correct params', async () => { - mockedTransport.mockImplementationOnce(() => { - return { - body: { - inference_results: [ - { - output: [ + mockedTransport.mockImplementation((args) => { + if (args.path === '/_plugins/_ml/agents/_search') { + return { + body: { + hits: { + total: { + value: 1, + }, + hits: [ { - name: 'memory_id', - result: 'foo', + _index: '.plugins-ml-agent', + _id: 'rootAgentId', }, ], }, - ], - }, - }; + }, + }; + } else { + return { + body: { + inference_results: [ + { + output: [ + { + name: 'memory_id', + result: 'foo', + }, + ], + }, + ], + }, + }; + } + }); + const result = await ollyChatService.regenerate({ + sessionId: 'sessionId', + interactionId: 'interactionId', }); - const result = await ollyChatService.regenerate( - { - sessionId: 'sessionId', - rootAgentId: 'rootAgentId', - interactionId: 'interactionId', - }, - contextMock - ); expect(mockedTransport.mock.calls).toMatchInlineSnapshot(` Array [ + Array [ + Object { + "body": Object { + "query": Object { + "term": Object { + "name.keyword": "test", + }, + }, + "sort": Object { + "created_time": "desc", + }, + }, + "method": "GET", + "path": "/_plugins/_ml/agents/_search", + }, + ], Array [ Object { "body": Object { @@ -162,18 +240,206 @@ describe('OllyChatService', () => { }); it('regenerate should throw error when transport.request throws error', async () => { + mockedTransport + .mockImplementationOnce(() => { + return { + body: { + hits: { + total: { + value: 1, + }, + hits: [ + { + _index: '.plugins-ml-agent', + _id: 'rootAgentId', + }, + ], + }, + }, + }; + }) + .mockImplementationOnce(() => { + throw new Error('error'); + }); + expect( + ollyChatService.regenerate({ + sessionId: 'sessionId', + interactionId: 'interactionId', + }) + ).rejects.toMatchInlineSnapshot(`[Error: error]`); + }); + + it('refetch the root agent id when executing agent throws 404 error', async () => { + mockedTransport + .mockImplementationOnce(() => { + return { + body: { + hits: { + total: { + value: 1, + }, + hits: [ + { + _index: '.plugins-ml-agent', + _id: 'rootAgentId', + }, + ], + }, + }, + }; + }) + .mockImplementationOnce(() => { + const meta: ApiResponse = { + body: { + error: { + type: 'resource_not_found_exception', + reason: 'Agent not found', + }, + status: 404, + }, + statusCode: 404, + }; + throw new ResponseError(meta); + }) + .mockImplementationOnce(() => { + return { + body: { + hits: { + total: { + value: 1, + }, + hits: [ + { + _index: '.plugins-ml-agent', + _id: 'rootAgentId', + }, + ], + }, + }, + }; + }) + .mockImplementationOnce(() => { + return { + body: { + inference_results: [ + { + output: [ + { + name: 'memory_id', + result: 'foo', + }, + ], + }, + ], + }, + }; + }); + const result = await ollyChatService.requestLLM({ + messages: [], + input: { + type: 'input', + contentType: 'text', + content: 'content', + }, + sessionId: '', + }); + expect(mockedTransport.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "body": Object { + "query": Object { + "term": Object { + "name.keyword": "test", + }, + }, + "sort": Object { + "created_time": "desc", + }, + }, + "method": "GET", + "path": "/_plugins/_ml/agents/_search", + }, + ], + Array [ + Object { + "body": Object { + "parameters": Object { + "question": "content", + "verbose": true, + }, + }, + "method": "POST", + "path": "/_plugins/_ml/agents/rootAgentId/_execute", + }, + Object { + "maxRetries": 0, + "requestTimeout": 300000, + }, + ], + Array [ + Object { + "body": Object { + "query": Object { + "term": Object { + "name.keyword": "test", + }, + }, + "sort": Object { + "created_time": "desc", + }, + }, + "method": "GET", + "path": "/_plugins/_ml/agents/_search", + }, + ], + Array [ + Object { + "body": Object { + "parameters": Object { + "question": "content", + "verbose": true, + }, + }, + "method": "POST", + "path": "/_plugins/_ml/agents/rootAgentId/_execute", + }, + Object { + "maxRetries": 0, + "requestTimeout": 300000, + }, + ], + ] + `); + expect(result).toMatchInlineSnapshot(` + Object { + "interactionId": "", + "memoryId": "foo", + "messages": Array [], + } + `); + }); + + it('fetching root agent id throws error', async () => { mockedTransport.mockImplementationOnce(() => { - throw new Error('error'); + return { + body: { + hits: { + total: { + value: 0, + }, + hits: [], + }, + }, + }; }); expect( - ollyChatService.regenerate( - { - sessionId: 'sessionId', - rootAgentId: 'rootAgentId', - interactionId: 'interactionId', - }, - contextMock - ) - ).rejects.toMatchInlineSnapshot(`[Error: error]`); + ollyChatService.regenerate({ + sessionId: 'sessionId', + interactionId: 'interactionId', + }) + ).rejects.toMatchInlineSnapshot( + `[Error: search root agent failed, reason: Error: cannot find any root agent by name: test]` + ); }); }); diff --git a/server/services/chat/olly_chat_service.ts b/server/services/chat/olly_chat_service.ts index 37ec41dd..1a555236 100644 --- a/server/services/chat/olly_chat_service.ts +++ b/server/services/chat/olly_chat_service.ts @@ -7,7 +7,11 @@ import { ApiResponse } from '@opensearch-project/opensearch'; import { RequestHandlerContext } from '../../../../../src/core/server'; import { IMessage, IInput } from '../../../common/types/chat_saved_object_attributes'; import { ChatService } from './chat_service'; -import { ML_COMMONS_BASE_API } from '../../utils/constants'; +import { + ML_COMMONS_BASE_API, + RESOURCE_NOT_FOUND_STATUS_CODE, + RESOURCE_NOT_FOUND_ERROR, +} from '../../utils/constants'; interface AgentRunPayload { question?: string; @@ -21,22 +25,86 @@ const INTERACTION_ID_FIELD = 'parent_interaction_id'; export class OllyChatService implements ChatService { static abortControllers: Map = new Map(); + private static rootAgentId: string | undefined; - private async requestAgentRun( - rootAgentId: string, - payload: AgentRunPayload, - context: RequestHandlerContext - ) { + constructor( + private readonly context: RequestHandlerContext, + private readonly rootAgentName: string + ) {} + + private async initRootAgent() { + OllyChatService.rootAgentId = await this.searchRootAgent(this.rootAgentName); + } + + private async searchRootAgent(rootAgentName: string): Promise { + try { + const requestParams = { + query: { + term: { + 'name.keyword': rootAgentName, + }, + }, + sort: { + created_time: 'desc', + }, + }; + + const opensearchClient = this.context.core.opensearch.client.asCurrentUser; + const response = await opensearchClient.transport.request({ + method: 'GET', + path: `${ML_COMMONS_BASE_API}/agents/_search`, + body: requestParams, + }); + + if (!response || response.body.hits.total.value === 0) { + throw new Error('cannot find any root agent by name: ' + rootAgentName); + } + return response.body.hits.hits[0]._id; + } catch (error) { + const errorMessage = JSON.stringify(error.meta?.body) || error; + throw new Error('search root agent failed, reason: ' + errorMessage); + } + } + + private async requestAgentRun(payload: AgentRunPayload) { if (payload.memory_id) { OllyChatService.abortControllers.set(payload.memory_id, new AbortController()); } - const opensearchClient = context.core.opensearch.client.asCurrentUser; + + // if rootAgentId has not been initialized yet, init rootAgentId firstly + if (!OllyChatService.rootAgentId) { + await this.initRootAgent(); + this.context.assistant_plugin.logger.info( + `root agent id has not been initialized yet, init it at the first time, current root agent id is:${OllyChatService.rootAgentId}` + ); + } + try { + return await this.callExecuteAgentAPI(payload); + } catch (error) { + if ( + error.meta?.statusCode === RESOURCE_NOT_FOUND_STATUS_CODE && + error.body.error.type === RESOURCE_NOT_FOUND_ERROR + ) { + const oldRootAgentId = OllyChatService.rootAgentId; + await this.initRootAgent(); + this.context.assistant_plugin.logger.info( + `cannot find the root agent id: ${oldRootAgentId}, try to fetch the new root agent id, now it is:${OllyChatService.rootAgentId}` + ); + return await this.callExecuteAgentAPI(payload); + } else { + throw error; + } + } + } + + private async callExecuteAgentAPI(payload: AgentRunPayload) { + const opensearchClient = this.context.core.opensearch.client.asCurrentUser; try { const agentFrameworkResponse = (await opensearchClient.transport.request( { method: 'POST', - path: `${ML_COMMONS_BASE_API}/agents/${rootAgentId}/_execute`, + path: `${ML_COMMONS_BASE_API}/agents/${OllyChatService.rootAgentId}/_execute`, body: { parameters: payload, }, @@ -78,15 +146,16 @@ export class OllyChatService implements ChatService { } } - public async requestLLM( - payload: { messages: IMessage[]; input: IInput; sessionId?: string; rootAgentId: string }, - context: RequestHandlerContext - ): Promise<{ + async requestLLM(payload: { + messages: IMessage[]; + input: IInput; + sessionId?: string; + }): Promise<{ messages: IMessage[]; memoryId: string; interactionId: string; }> { - const { input, sessionId, rootAgentId } = payload; + const { input, sessionId } = payload; const parametersPayload: Pick = { question: input.content, @@ -97,14 +166,14 @@ export class OllyChatService implements ChatService { parametersPayload.memory_id = sessionId; } - return await this.requestAgentRun(rootAgentId, parametersPayload, context); + return await this.requestAgentRun(parametersPayload); } - async regenerate( - payload: { sessionId: string; interactionId: string; rootAgentId: string }, - context: RequestHandlerContext - ): Promise<{ messages: IMessage[]; memoryId: string; interactionId: string }> { - const { sessionId, interactionId, rootAgentId } = payload; + async regenerate(payload: { + sessionId: string; + interactionId: string; + }): Promise<{ messages: IMessage[]; memoryId: string; interactionId: string }> { + const { sessionId, interactionId } = payload; const parametersPayload: Pick< AgentRunPayload, 'regenerate_interaction_id' | 'verbose' | 'memory_id' @@ -114,7 +183,7 @@ export class OllyChatService implements ChatService { verbose: true, }; - return await this.requestAgentRun(rootAgentId, parametersPayload, context); + return await this.requestAgentRun(parametersPayload); } abortAgentExecution(sessionId: string) { @@ -122,4 +191,9 @@ export class OllyChatService implements ChatService { OllyChatService.abortControllers.get(sessionId)?.abort(); } } + + // only used for test + resetRootAgentId() { + OllyChatService.rootAgentId = undefined; + } } diff --git a/server/types.ts b/server/types.ts index 8b0dd8de..8f869831 100644 --- a/server/types.ts +++ b/server/types.ts @@ -40,7 +40,7 @@ export interface MessageParser { export interface RoutesOptions { messageParsers: MessageParser[]; - rootAgentId?: string; + rootAgentName?: string; } declare module '../../../src/core/server' { diff --git a/server/utils/constants.ts b/server/utils/constants.ts index 3b442fb8..8592ae82 100644 --- a/server/utils/constants.ts +++ b/server/utils/constants.ts @@ -4,3 +4,5 @@ */ export const ML_COMMONS_BASE_API = '/_plugins/_ml'; +export const RESOURCE_NOT_FOUND_STATUS_CODE = 404; +export const RESOURCE_NOT_FOUND_ERROR = 'resource_not_found_exception';