Skip to content

[Observability AI Assistant] Screen Context#175885

Merged
dgieselaar merged 25 commits intoelastic:mainfrom
CoenWarmer:feat/chat-context
Feb 13, 2024
Merged

[Observability AI Assistant] Screen Context#175885
dgieselaar merged 25 commits intoelastic:mainfrom
CoenWarmer:feat/chat-context

Conversation

@CoenWarmer
Copy link
Contributor

@CoenWarmer CoenWarmer commented Jan 30, 2024

Summary

This adds functionality to allow consumers of the AI Assistant for Observability to add context to the LLM conversation, at the start of a conversation and contextual after every prompt.

Screen.Recording.2024-01-30.at.14.37.41.mov

image

How to use

The service now exposes a setApplicationContext function, that returns a hook to unregister the context. Here's an example:

Consumers can use this to add context relevant to the route or settings of Kibana that might be relevant for the LLM within that conversation.

Example:

useEffect(() => {
  return setApplicationContext({
    data: [
      {
        name: 'top_transactions',
        description: 'The visible transaction groups',
        value: mainStatistics.transactionGroups.map((group) => {
          return {
            name: group.name,
            alertsCount: group.alertsCount,
          };
        }),
      },
    ],
  });
}, [setApplicationContext, mainStatistics]);

By default the URL that the user is currently on is always included in the context so the Assistant will always take that into account.

Details for reviewers

  • recall function has been renamed to context
  • context function now returns both Knowledge base entries as well as chat context that is set by setApplicationContext.[Security Solution] GenAI API Integration Tests #176357
  • part of the function logic was moved from the ObservabilityAIAssistantService into the ChatFunctionClient, for easier testing

@CoenWarmer CoenWarmer requested a review from a team January 30, 2024 13:36
@ghost
Copy link

ghost commented Jan 30, 2024

🤖 GitHub comments

Expand to view the GitHub comments

Just comment with:

  • /oblt-deploy : Deploy a Kibana instance using the Observability test environments.
  • /oblt-deploy-serverless : Deploy a serverless Kibana instance using the Observability test environments.
  • run elasticsearch-ci/docs : Re-trigger the docs validation. (use unformatted text in the comment!)

@CoenWarmer CoenWarmer added the release_note:skip Skip the PR/issue when compiling release notes label Jan 30, 2024
@CoenWarmer CoenWarmer changed the title Add chatContext functionality [Observablity AI Assistant] Add chat context functionality Jan 30, 2024

const client = await service.getClient({ request });

client.setChatContext(params.body.chatContext);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should do this on the server (having side-effects). I also don't think we need it for chat, just /chat/complete. Let's make it a property of client.complete({ chatContext, ... })

Copy link
Contributor Author

@CoenWarmer CoenWarmer Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Could you elaborate why this doesn't need to happen on the server when other functions do, from a conceptual standpoint? What is it about this that makes it doesn't need to happen on the server?
  2. How would that work when the context function now retrieves both knowledge base entries and chat context? The kb entry getting happens on the server, so how should it then get stuff from the client here?

Re: also adding it to /chat: wouldn't having that also unlock augmenting the contextual insights with context?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate why this doesn't need to happen on the server when other functions do, from a conceptual standpoint? What is it about this that makes it doesn't need to happen on the server?

How would that work when the context function now retrieves both knowledge base entries and chat context? The kb entry getting happens on the server, so how should it then get stuff from the client here?

Sorry, I realize my wording is ambiguous. What I mean is that I don't think we should have side-effects on the server. The client is unique per request, but I still think it is unexpected for it to have side-effects like this. Maybe side-effects is the wrong term anyway, but what I mean is that it should be a property of /chat/complete. E.g. we don't use setMessages either.

Re: also adding it to /chat: wouldn't having that also unlock augmenting the contextual insights with context?

The contextual insights use /chat/complete.

@CoenWarmer CoenWarmer changed the title [Observablity AI Assistant] Add chat context functionality [Observability AI Assistant] Add chat context functionality Jan 31, 2024
@CoenWarmer CoenWarmer requested a review from dgieselaar January 31, 2024 12:48
Copy link
Contributor

@dgieselaar dgieselaar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've not finished going through all of this, so consider this early feedback - that I expect will amount to some significant changes.

initialMessages: Message[];
connectorId: string;
}) {
const service = useObservabilityAIAssistant();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the plugin service, and not the chat service?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the chat service is not available until after the chat has started.


import { ObservabilityAIAssistantService } from '../types';

export function useObservabilityAIAssistantChatContext(service: ObservabilityAIAssistantService) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to run a function here on unmount to remove the context set by the consuming component.

shareReplay()
);
},
complete({ chatContext, connectorId, conversationId, messages, persist, signal }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is just re-sorting, but it makes it harder to see the diff

getLicense: () => licenseStart.license$,
getLicenseManagementLocator: () => shareStart,
setChatContext: (newChatContext: ChatContext) => {
chatContext = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed elsewhere, this just keeps adding context indefinitely


const client = await service.getClient({ request });

client.setChatContext(params.body.chatContext);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate why this doesn't need to happen on the server when other functions do, from a conceptual standpoint? What is it about this that makes it doesn't need to happen on the server?

How would that work when the context function now retrieves both knowledge base entries and chat context? The kb entry getting happens on the server, so how should it then get stuff from the client here?

Sorry, I realize my wording is ambiguous. What I mean is that I don't think we should have side-effects on the server. The client is unique per request, but I still think it is unexpected for it to have side-effects like this. Maybe side-effects is the wrong term anyway, but what I mean is that it should be a property of /chat/complete. E.g. we don't use setMessages either.

Re: also adding it to /chat: wouldn't having that also unlock augmenting the contextual insights with context?

The contextual insights use /chat/complete.

@CoenWarmer CoenWarmer requested a review from dgieselaar January 31, 2024 17:01
@CoenWarmer
Copy link
Contributor Author

I've not finished going through all of this, so consider this early feedback - that I expect will amount to some significant changes.

I'm surprised 20 files is too much to go through in one go. Wouldn't it make sense to finish it and get a complete picture?

In any case, I replied to one of your remarks that I think is valuable here. FWIW, I also wanted to have it on the client and not the service but the client is only available after the chat is started. That would mean consumers would have to pass in the client to the hook which isn't great either. Let me know your thoughts.

@dgieselaar
Copy link
Contributor

@CoenWarmer I have other things to do :) I'm doing this to give you quicker feedback so you'll be able to work on it.

@CoenWarmer CoenWarmer requested a review from a team as a code owner February 7, 2024 14:22
@CoenWarmer CoenWarmer requested a review from a team February 7, 2024 14:22
@botelastic botelastic bot added Team:obs-ux-infra_services - DEPRECATED DEPRECATED - Use Team:obs-presentation. Team:actionable-obs Formerly "obs-ux-management", responsible for SLO, o11y alerting, significant events, & synthetics. labels Feb 7, 2024
@elasticmachine
Copy link
Contributor

Pinging @elastic/obs-ux-management-team (Team:obs-ux-management)

@elasticmachine
Copy link
Contributor

Pinging @elastic/obs-ux-infra_services-team (Team:obs-ux-infra_services)

@CoenWarmer CoenWarmer marked this pull request as draft February 7, 2024 14:25
@CoenWarmer CoenWarmer changed the title [Observability AI Assistant] Add chat context functionality [Observability AI Assistant] Chat Context + Suggestions functionality Feb 7, 2024
@dgieselaar
Copy link
Contributor

Thanks for your work so far @CoenWarmer! Discussed with @sorenlouv over Zoom, I plan on making a couple of changes:

  • reverting the suggestions so we can split that out into a separate PR. Looks like the work is not really related and I would rather have a separate review process for it.
  • We'll flatten the context object to strings that we'll concatenate. This should feel more natural (e.g., consider "The user is looking at the opbeans-go service. There are no anomalies for this service" vs JSON that describes the same).
  • I'll add support for data, which are pieces of data that we send over to the /chat/complete endpoint, keep in memory, and tell the LLM that it can request it. This saves on tokens initially, but still allows for the LLM to "see" what the user is looking at when it wants to.

Comment on lines +269 to +271
const setApplicationContext =
useApmPluginContext().observabilityAIAssistant.service
.setApplicationContext;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: with destructuring we might be able to get a one-liner.

Suggested change
const setApplicationContext =
useApmPluginContext().observabilityAIAssistant.service
.setApplicationContext;
const { setApplicationContext } = useApmPluginContext().observabilityAIAssistant.service;

});
}

return setApplicationContext({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this chatContext? It will be helpful for consumers to highlight that this is context for the AI Assistant - and not application context in general.

Suggested change
return setApplicationContext({
return setChatContext({

Also: I think we to consider the different types of "context" and how we refer to them. Afaict we have:

  • chat context supplied by consumers
  • knowledge base articles
  • data from connectors (third parties)

Is all of it context? Is all of it knowledge base?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was chatContext originally, but I'm thinking there are use cases without chat, e.g. on a one-off interaction with the LLM, like contextual insights, or generating an ES|QL query. I agree that applicationContext is not very clear. Maybe setAssistantContext?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setAssistantContext is definitely better.

name: 'recall',
arguments: '{"queries":[],"categories":[]}',
name: 'context',
arguments: '{"learnings": { "queries":[],"categories":[]} }',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the nesting under learnings?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, this is a mistake. will correct

const screenDescription = appContexts.map((context) => context.description).join('\n\n');

const nonEmptyQueries = compact(queries);
const content = { screen_description: screenDescription, learnings: [] };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about moving the dataWithinTokenLimit to the getContext function?

Suggested change
const content = { screen_description: screenDescription, learnings: [] };
// any data that falls within the token limit, send it automatically
const dataWithinTokenLimit = compact(appContexts.flatMap((context) => context.data)).filter(
(data) => encode(JSON.stringify(data.value)).length <= MAX_TOKEN_COUNT_FOR_DATA_ON_SCREEN
);
const content = {
screen_description: screenDescription,
data_on_screen: dataWithinTokenLimit,
learnings: [],
};

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +129 to +136
// any data that falls within the token limit, send it automatically

const dataWithinTokenLimit = compact(
appContexts.flatMap((context) => context.data)
).filter(
(data) =>
encode(JSON.stringify(data.value)).length <= MAX_TOKEN_COUNT_FOR_DATA_ON_SCREEN
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not part of the getContext function?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't part of it because I originally implemented it as separate messages (get_data_on_screen function calls). But I was worried that that would cause the LLM to no longer call get_data_on_screen, so I removed it again. I've moved it to the getContext function.

});

const response$ = await client.complete({
const response$ = client.complete({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This dangling await has been triggering severe OCD!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄 @miltonhultgren was complaining about it as well

if (allData.length) {
this.registerFunction(
{
name: 'get_data_on_screen',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to make sure I understand this correctly: The LLM can now get the chat context in two ways:

  • calling the context function will return the chat context (possibly truncated due to token limit) or
  • calling the get_data_on_screen function which will return the untruncated chat context

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw. it looks like get_data_on_screen doesn't include the appContexts.description (aka "screenDescription"). Is that intentional? Afaict the context for the SLOList only has screen description and no data attribute so that would result in no context being provided here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the descriptions are automatically added in the context function call, so there is no need for the LLM to request it.

`);

return setApplicationContext({
description,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should we call the top level description for screenDescription to indicate that it should describe what the user is looking at, whereas the description under data is to describe what the data entities are.

Suggested change
description,
screenDescription,

@dgieselaar dgieselaar changed the title [Observability AI Assistant] App Context [Observability AI Assistant] Screen Context Feb 13, 2024
Copy link
Member

@sorenlouv sorenlouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome improvement! Excited to see the impact of this!

@dgieselaar dgieselaar enabled auto-merge (squash) February 13, 2024 12:58
@kibana-ci
Copy link

💛 Build succeeded, but was flaky

Failed CI Steps

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
observability 598 599 +1

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
observabilityAIAssistant 73 76 +3

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
apm 3.2MB 3.2MB +1.2KB
observability 638.5KB 640.8KB +2.2KB
observabilityAIAssistant 168.5KB 168.8KB +263.0B
total +3.7KB

Public APIs missing exports

Total count of every type that is part of your API that should be exported but is not. This will cause broken links in the API documentation system. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats exports for more detailed information.

id before after diff
observabilityAIAssistant 13 14 +1

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
observability 102.8KB 102.8KB -48.0B
observabilityAIAssistant 14.5KB 14.6KB +101.0B
total +53.0B
Unknown metric groups

API count

id before after diff
observabilityAIAssistant 75 78 +3

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@dgieselaar dgieselaar requested a review from a team February 13, 2024 14:53

${
alertDetail.formatted.reason
? `The reason given for the alert is ${alertDetail.formatted.reason}.`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed, everything we sent to the LLM needs to be in English.

return;
}

const screenDescription = dedent(`The user is looking at an ${
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shahzad31 We always use English when communicating with the LLM (think of this as an API call where you wouldn't translate the params either)

The user is looking at the detail page for the following SLO

Name: ${slo.name}.
Id: ${slo.id}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we also need to slo.instanceId if it's not equal to *

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's available in the metadata below

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What metadata are you referring to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if using the actual doc fields slo.name, slo.id etc, would help the LLM be able to match the data to parameters for functions when those parameters use the field name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the data prop - that's also sent over to the LLM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: your second question: yes, that's useful to the LLM, but also available in the data prop

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dominiqueclarke sorry I spoke to soon, I have not actually verified whether field names are available in the metadata. Where are the field names used (outside of SOs)?

Copy link
Contributor

@dominiqueclarke dominiqueclarke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM just wondering about the instanceId value as Shahzad has mentioned but SLO team can always refine the data provided on the pages we own.

@dgieselaar dgieselaar merged commit 853b5f6 into elastic:main Feb 13, 2024
@kibanamachine kibanamachine added the backport:skip This PR does not require backporting label Feb 13, 2024
CoenWarmer added a commit to CoenWarmer/kibana that referenced this pull request Feb 15, 2024
## Summary

This adds functionality to allow consumers of the AI Assistant for
Observability to add context to the LLM conversation, at the start of a
conversation and contextual after every prompt.


https://github.com/elastic/kibana/assets/535564/b4d62897-d701-4c23-b90b-464cad21e9d0


![image](https://github.com/elastic/kibana/assets/352732/85a7e27a-e715-4273-a3a3-0dd68a7c9c5c)


## How to use
The service now exposes a `setApplicationContext` function, that returns
a hook to unregister the context. Here's an example:



Consumers can use this to add context relevant to the route or settings
of Kibana that might be relevant for the LLM within that conversation.

Example:

```ts
useEffect(() => {
  return setApplicationContext({
    data: [
      {
        name: 'top_transactions',
        description: 'The visible transaction groups',
        value: mainStatistics.transactionGroups.map((group) => {
          return {
            name: group.name,
            alertsCount: group.alertsCount,
          };
        }),
      },
    ],
  });
}, [setApplicationContext, mainStatistics]);
```

By default the URL that the user is currently on is always included in
the context so the Assistant will always take that into account.

## Details for reviewers
- `recall` function has been renamed to `context`
- `context` function now returns both Knowledge base entries as well as
chat context that is set by `setApplicationContext`.elastic#176357
- part of the function logic was moved from the
ObservabilityAIAssistantService into the ChatFunctionClient, for easier
testing

---------

Co-authored-by: Dario Gieselaar <dario.gieselaar@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
fkanout pushed a commit to fkanout/kibana that referenced this pull request Mar 4, 2024
## Summary

This adds functionality to allow consumers of the AI Assistant for
Observability to add context to the LLM conversation, at the start of a
conversation and contextual after every prompt.


https://github.com/elastic/kibana/assets/535564/b4d62897-d701-4c23-b90b-464cad21e9d0


![image](https://github.com/elastic/kibana/assets/352732/85a7e27a-e715-4273-a3a3-0dd68a7c9c5c)


## How to use
The service now exposes a `setApplicationContext` function, that returns
a hook to unregister the context. Here's an example:



Consumers can use this to add context relevant to the route or settings
of Kibana that might be relevant for the LLM within that conversation.

Example:

```ts
useEffect(() => {
  return setApplicationContext({
    data: [
      {
        name: 'top_transactions',
        description: 'The visible transaction groups',
        value: mainStatistics.transactionGroups.map((group) => {
          return {
            name: group.name,
            alertsCount: group.alertsCount,
          };
        }),
      },
    ],
  });
}, [setApplicationContext, mainStatistics]);
```

By default the URL that the user is currently on is always included in
the context so the Assistant will always take that into account.

## Details for reviewers
- `recall` function has been renamed to `context`
- `context` function now returns both Knowledge base entries as well as
chat context that is set by `setApplicationContext`.elastic#176357
- part of the function logic was moved from the
ObservabilityAIAssistantService into the ChatFunctionClient, for easier
testing

---------

Co-authored-by: Dario Gieselaar <dario.gieselaar@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting release_note:skip Skip the PR/issue when compiling release notes Team:actionable-obs Formerly "obs-ux-management", responsible for SLO, o11y alerting, significant events, & synthetics. Team:obs-ux-infra_services - DEPRECATED DEPRECATED - Use Team:obs-presentation. v8.13.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants