Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] Refactor: get root agent id by calling api (#128) #129

Merged
merged 1 commit into from
Feb 4, 2024
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

- Add support for registerMessageParser ([#5](https://github.com/opensearch-project/dashboards-assistant/pull/5))
- Change implementation of basic_input_output to built-in parser ([#10](https://github.com/opensearch-project/dashboards-assistant/pull/10))
- Add interactions into ChatState and pass specific interaction into message_bubble ([#12](https://github.com/opensearch-project/dashboards-assistant/pull/12))
- Add interactions into ChatState and pass specific interaction into message_bubble ([#12](https://github.com/opensearch-project/dashboards-assistant/pull/12))
- Refactor the code to get root agent id by calling the API in ml-commons plugin ([#128](https://github.com/opensearch-project/dashboards-assistant/pull/128))
26 changes: 16 additions & 10 deletions GETTING_STARTED_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@

### How to run assistant on your own machine
Below are the set of steps to run OpenSearch and OpenSearch dashboards with the OpenSearch assistant and the query generation functionality in the Observability Log Explorer page correctly on the cluster.
**Note** that the `feature/langchain` is the branch used in this guide.

1. Follow steps here to setup docker for OpenSearch: https://opensearch.org/docs/latest/install-and-configure/install-opensearch/docker/
1. Note: When running docker pull, use this command instead: `docker pull public.ecr.aws/w1m7p7g2/opensearch-reinvent2023:latest`
2. Note: This docker image may get incrementally updated over time.


2. Follow steps here to setup docker to OpenSearch Dashboards: https://opensearch.org/docs/latest/install-and-configure/install-dashboards/docker/
1. Note: When running docker pull, use this command instead for OSD: `docker pull public.ecr.aws/w1m7p7g2/opensearch-dashboards-reinvent2023:latest`
2. Note: This docker image may get incrementally updated over time.
3. If you want to enable the chat assistant feature, set `assistant.chat.enabled` to `true` in the `opensearch_dashboards.yml` file.
1. Setup a 2.12+ OpenSearch cluster with OpenSearch Dashboards by following the options here: https://opensearch.org/docs/latest/install-and-configure/
1. Note: If you are using a min distribution, there are required OpenSearch and OpenSearch Dashboards plugin to run the assistant.
1. Required OpenSearch plugins: ML-Commons, Flow Framework, Skill, SQL, and Observability
2. Required OpenSearch Dashboard plugins: Dashboard Assistant, Dashboard Observability
2. Enable the following settings to enable the features:
1. To enable the chat assistant feature, set `assistant.chat.enabled` to `true` in the `opensearch_dashboards.yml` file, and config the root agent id by calling the api as follows:
```
PUT .plugins-ml-config/_doc/os_chat
{
"type":"os_chat_root_agent",
"configuration":{
"agent_id": "your root agent id"
}
}
```
2. To enable the query assistant feature, set `observability.query_assist.enabled` to `true` and `observability.query_assist.ppl_agent_name` to `"PPL agent"` in the `opensearch_dashboards.yml` file.
3. After OpenSearch and OpenSearch Dashboards are running, we will setup ML Commons to connect to the LLM model
4. Run ML commons on Data node
```
Expand Down
1 change: 0 additions & 1 deletion public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ interface PublicConfig {
chat: {
// whether chat feature is enabled, UI should hide if false
enabled: boolean;
rootAgentName?: string;
};
}

Expand Down
6 changes: 0 additions & 6 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ const assistantConfig = {
schema: schema.object({
chat: schema.object({
enabled: schema.boolean({ defaultValue: false }),
rootAgentName: schema.conditional(
schema.siblingRef('enabled'),
true,
schema.string(),
schema.maybe(schema.string())
),
}),
}),
};
Expand Down
10 changes: 0 additions & 10 deletions server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ 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 { AgentNameNotFoundError } from './routes/chat_routes';

export class AssistantPlugin implements Plugin<AssistantPluginSetup, AssistantPluginStart> {
private readonly logger: Logger;
Expand All @@ -33,14 +32,6 @@ export class AssistantPlugin implements Plugin<AssistantPluginSetup, AssistantPl
.pipe(first())
.toPromise();

/**
* Check if user enable the chat without specifying a root agent name.
* If so, gives a warning for guidance.
*/
if (config.chat.enabled && !config.chat.rootAgentName) {
this.logger.warn(AgentNameNotFoundError);
}

const router = core.http.createRouter();

core.http.registerRouteHandlerContext('assistant_plugin', () => {
Expand All @@ -53,7 +44,6 @@ export class AssistantPlugin implements Plugin<AssistantPluginSetup, AssistantPl
// Register server side APIs
setupRoutes(router, {
messageParsers: this.messageParsers,
rootAgentName: config.chat.rootAgentName,
});

core.capabilities.registerProvider(() => ({
Expand Down
18 changes: 3 additions & 15 deletions server/routes/chat_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ const llmRequestRoute = {
};
export type LLMRequestSchema = TypeOf<typeof llmRequestRoute.validate.body>;

export const AgentNameNotFoundError =
'rootAgentName is required, please specify one in opensearch_dashboards.yml';

const getConversationRoute = {
path: `${ASSISTANT_API.CONVERSATION}/{conversationId}`,
validate: {
Expand Down Expand Up @@ -135,8 +132,7 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions)
context.core.opensearch.client.asCurrentUser,
routeOptions.messageParsers
);
const createChatService = (context: RequestHandlerContext, rootAgentName: string) =>
new OllyChatService(context, rootAgentName);
const createChatService = (context: RequestHandlerContext) => new OllyChatService(context);

router.post(
llmRequestRoute,
Expand All @@ -145,13 +141,9 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions)
request,
response
): Promise<IOpenSearchDashboardsResponse<HttpResponsePayload | ResponseError>> => {
if (!routeOptions.rootAgentName) {
context.assistant_plugin.logger.error(AgentNameNotFoundError);
return response.custom({ statusCode: 400, body: AgentNameNotFoundError });
}
const { messages = [], input, conversationId: conversationIdInRequestBody } = request.body;
const storageService = createStorageService(context);
const chatService = createChatService(context, routeOptions.rootAgentName);
const chatService = createChatService(context);

let outputs: Awaited<ReturnType<ChatService['requestLLM']>> | undefined;

Expand Down Expand Up @@ -344,13 +336,9 @@ export function registerChatRoutes(router: IRouter, routeOptions: RoutesOptions)
request,
response
): Promise<IOpenSearchDashboardsResponse<HttpResponsePayload | ResponseError>> => {
if (!routeOptions.rootAgentName) {
context.assistant_plugin.logger.error(AgentNameNotFoundError);
return response.custom({ statusCode: 400, body: AgentNameNotFoundError });
}
const { conversationId, interactionId } = request.body;
const storageService = createStorageService(context);
const chatService = createChatService(context, routeOptions.rootAgentName);
const chatService = createChatService(context);

let outputs: Awaited<ReturnType<ChatService['regenerate']>> | undefined;

Expand Down
51 changes: 2 additions & 49 deletions server/routes/regenerate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, AgentNameNotFoundError } from './chat_routes';
import { registerChatRoutes, RegenerateSchema } from './chat_routes';
import { ASSISTANT_API } from '../../common/constants/llm';

const mockedLogger = loggerMock.create();

describe('regenerate route when rootAgentName is provided', () => {
describe('test regenerate route', () => {
const router = new Router(
'',
mockedLogger,
Expand All @@ -31,7 +31,6 @@ describe('regenerate route when rootAgentName is provided', () => {
);
registerChatRoutes(router, {
messageParsers: [],
rootAgentName: 'foo',
});
const regenerateRequest = (payload: RegenerateSchema) =>
triggerHandler(router, {
Expand Down Expand Up @@ -164,49 +163,3 @@ describe('regenerate route when rootAgentName is provided', () => {
`);
});
});

describe('regenerate route when rootAgentName is not provided', () => {
const router = new Router(
'',
mockedLogger,
enhanceWithContext({
assistant_plugin: {
logger: mockedLogger,
},
})
);
registerChatRoutes(router, {
messageParsers: [],
});
const regenerateRequest = (payload: RegenerateSchema) =>
triggerHandler(router, {
method: 'put',
path: ASSISTANT_API.REGENERATE,
req: httpServerMock.createRawRequest({
payload: JSON.stringify(payload),
}),
});
beforeEach(() => {
loggerMock.clear(mockedLogger);
});

it('return 400', async () => {
const result = (await regenerateRequest({
interactionId: 'bar',
conversationId: 'foo',
})) as Boom;
expect(mockedLogger.error).toBeCalledTimes(1);
expect(mockedLogger.error).toBeCalledWith(AgentNameNotFoundError);
expect(result.output).toMatchInlineSnapshot(`
Object {
"headers": Object {},
"payload": Object {
"error": "Bad Request",
"message": "rootAgentName is required, please specify one in opensearch_dashboards.yml",
"statusCode": 400,
},
"statusCode": 400,
}
`);
});
});
58 changes: 2 additions & 56 deletions server/routes/send_message.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, AgentNameNotFoundError } from './chat_routes';
import { registerChatRoutes, LLMRequestSchema } from './chat_routes';
import { ASSISTANT_API } from '../../common/constants/llm';

const mockedLogger = loggerMock.create();

describe('send_message route when rootAgentName is provided', () => {
describe('test send_message route', () => {
const router = new Router(
'',
mockedLogger,
Expand All @@ -31,7 +31,6 @@ describe('send_message route when rootAgentName is provided', () => {
);
registerChatRoutes(router, {
messageParsers: [],
rootAgentName: 'foo',
});
const sendMessageRequest = (payload: LLMRequestSchema) =>
triggerHandler(router, {
Expand Down Expand Up @@ -260,56 +259,3 @@ describe('send_message route when rootAgentName is provided', () => {
`);
});
});

describe('send_message route when rootAgentName is not provided', () => {
const router = new Router(
'',
mockedLogger,
enhanceWithContext({
assistant_plugin: {
logger: mockedLogger,
},
})
);
registerChatRoutes(router, {
messageParsers: [],
});
const sendMessageRequest = (payload: LLMRequestSchema) =>
triggerHandler(router, {
method: 'post',
path: ASSISTANT_API.SEND_MESSAGE,
req: httpServerMock.createRawRequest({
payload: JSON.stringify(payload),
}),
});
beforeEach(() => {
loggerMock.clear(mockedLogger);
});

it('return 400', async () => {
const result = (await sendMessageRequest({
input: {
content: '1',
contentType: 'text',
type: 'input',
context: {
appId: '',
},
},
conversationId: 'foo',
})) as Boom;
expect(mockedLogger.error).toBeCalledTimes(1);
expect(mockedLogger.error).toBeCalledWith(AgentNameNotFoundError);
expect(result.output).toMatchInlineSnapshot(`
Object {
"headers": Object {},
"payload": Object {
"error": "Bad Request",
"message": "rootAgentName is required, please specify one in opensearch_dashboards.yml",
"statusCode": 400,
},
"statusCode": 400,
}
`);
});
});
Loading
Loading