Skip to content

Commit

Permalink
[Observability AI Assistant]: Summarise & recall (elastic#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgieselaar authored Aug 3, 2023
1 parent ff5ffc2 commit 006e1d6
Show file tree
Hide file tree
Showing 23 changed files with 740 additions and 94 deletions.
2 changes: 1 addition & 1 deletion packages/kbn-io-ts-utils/src/to_boolean_rt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import * as t from 'io-ts';

export const toBooleanRt = new t.Type<boolean, unknown, unknown>(
export const toBooleanRt = new t.Type<boolean, boolean, unknown>(
'ToBoolean',
t.boolean.is,
(input) => {
Expand Down
10 changes: 10 additions & 0 deletions x-pack/plugins/observability_ai_assistant/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface Conversation {
labels: Record<string, string>;
numeric_labels: Record<string, number>;
namespace: string;
public: boolean;
}

export type ConversationRequestBase = Omit<Conversation, 'user' | 'conversation' | 'namespace'> & {
Expand All @@ -58,6 +59,15 @@ export type ConversationRequestBase = Omit<Conversation, 'user' | 'conversation'
export type ConversationCreateRequest = ConversationRequestBase;
export type ConversationUpdateRequest = ConversationRequestBase & { conversation: { id: string } };

export interface KnowledgeBaseEntry {
'@timestamp': string;
id: string;
text: string;
confidence: 'low' | 'medium' | 'high';
is_correction: boolean;
public: boolean;
}

type CompatibleJSONSchema = Exclude<JSONSchema, boolean>;

export interface ContextDefinition {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { ComponentStory } from '@storybook/react';
import React from 'react';
import { Observable } from 'rxjs';
import { getSystemMessage } from '../../service/get_system_message';
import { ObservabilityAIAssistantService } from '../../types';
import { ChatBody as Component } from './chat_body';

Expand All @@ -34,7 +35,8 @@ const defaultProps: ChatBodyProps = {
},
labels: {},
numeric_labels: {},
messages: [],
messages: [getSystemMessage()],
public: false,
},
connectors: {
connectors: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { ComponentStory } from '@storybook/react';
import React from 'react';
import { getSystemMessage } from '../../service/get_system_message';
import { KibanaReactStorybookDecorator } from '../../utils/storybook_decorator';
import { ChatFlyout as Component } from './chat_flyout';

Expand All @@ -33,9 +34,10 @@ const defaultProps: ChatFlyoutProps = {
conversation: {
title: 'How is this working',
},
messages: [],
messages: [getSystemMessage()],
labels: {},
numeric_labels: {},
public: false,
},
onClose: () => {},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ function ChatContent({ messages, connectorId }: { messages: Message[]; connector
},
labels: {},
numeric_labels: {},
public: false,
};
}, [pendingMessage, messages]);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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 { Serializable } from '@kbn/utility-types';
import type { RegisterFunctionDefinition } from '../../common/types';
import type { ObservabilityAIAssistantService } from '../types';

export function registerElasticsearchFunction({
service,
registerFunction,
}: {
service: ObservabilityAIAssistantService;
registerFunction: RegisterFunctionDefinition;
}) {
registerFunction(
{
name: 'elasticsearch',
contexts: ['core'],
description: 'Call Elasticsearch APIs on behalf of the user',
parameters: {
type: 'object',
properties: {
method: {
type: 'string',
description: 'The HTTP method of the Elasticsearch endpoint',
enum: ['GET', 'PUT', 'POST', 'DELETE', 'PATCH'] as const,
},
path: {
type: 'string',
description: 'The path of the Elasticsearch endpoint, including query parameters',
},
},
required: ['method' as const, 'path' as const],
},
},
({ arguments: { method, path, body } }, signal) => {
return service
.callApi(`POST /internal/observability_ai_assistant/functions/elasticsearch`, {
signal,
params: {
body: {
method,
path,
body,
},
},
})
.then((response) => ({ content: response as Serializable }));
}
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { RegisterContextDefinition, RegisterFunctionDefinition } from '../../common/types';
import type { ObservabilityAIAssistantService } from '../types';
import { registerElasticsearchFunction } from './elasticsearch';
import { registerRecallFunction } from './recall';
import { registerSetupKbFunction } from './setup_kb';
import { registerSummarisationFunction } from './summarise';

export function registerFunctions({
registerFunction,
registerContext,
service,
}: {
registerFunction: RegisterFunctionDefinition;
registerContext: RegisterContextDefinition;
service: ObservabilityAIAssistantService;
}) {
registerContext({
name: 'core',
description:
'Core functions, like calling Elasticsearch APIs, storing embeddables for instructions or creating base visualisations.',
});

registerElasticsearchFunction({ service, registerFunction });
registerSummarisationFunction({ service, registerFunction });
registerRecallFunction({ service, registerFunction });
registerSetupKbFunction({ service, registerFunction });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 { Serializable } from '@kbn/utility-types';
import type { RegisterFunctionDefinition } from '../../common/types';
import type { ObservabilityAIAssistantService } from '../types';

export function registerRecallFunction({
service,
registerFunction,
}: {
service: ObservabilityAIAssistantService;
registerFunction: RegisterFunctionDefinition;
}) {
registerFunction(
{
name: 'recall',
contexts: ['core'],
description:
'Use this function to recall earlier learnings. Anything you will summarise can be retrieved again later via this function.',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The query for the semantic search',
},
},
required: ['query' as const],
},
},
({ arguments: { query } }, signal) => {
return service
.callApi('POST /internal/observability_ai_assistant/functions/recall', {
params: {
body: {
query,
},
},
signal,
})
.then((response) => ({ content: response as unknown as Serializable }));
}
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 { Serializable } from '@kbn/utility-types';
import type { RegisterFunctionDefinition } from '../../common/types';
import type { ObservabilityAIAssistantService } from '../types';

export function registerSetupKbFunction({
service,
registerFunction,
}: {
service: ObservabilityAIAssistantService;
registerFunction: RegisterFunctionDefinition;
}) {
registerFunction(
{
name: 'setup_kb',
contexts: ['core'],
description:
'Use this function to set up the knowledge base. ONLY use this if you got an error from the recall or summarise function, or if the user has explicitly requested it. Note that it might take a while (e.g. ten minutes) until the knowledge base is available. Assume it will not be ready for the rest of the current conversation.',
parameters: {
type: 'object',
properties: {},
},
},
({}, signal) => {
return service
.callApi('POST /internal/observability_ai_assistant/functions/setup_kb', {
signal,
})
.then((response) => ({ content: response as unknown as Serializable }));
}
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 { RegisterFunctionDefinition } from '../../common/types';
import type { ObservabilityAIAssistantService } from '../types';

export function registerSummarisationFunction({
service,
registerFunction,
}: {
service: ObservabilityAIAssistantService;
registerFunction: RegisterFunctionDefinition;
}) {
registerFunction(
{
name: 'summarise',
contexts: ['core'],
description:
'Use this function to summarise things learned from the conversation. You can score the learnings with a confidence metric, whether it is a correction on a previous learning. An embedding will be created that you can recall later with a semantic search. There is no need to ask the user for permission to store something you have learned, unless you do not feel confident.',
parameters: {
type: 'object',
properties: {
id: {
type: 'string',
description:
'An id for the document. This should be a short human-readable keyword field with only alphabetic characters and underscores, that allow you to update it later.',
},
text: {
type: 'string',
description:
'A human-readable summary of what you have learned, described in such a way that you can recall it later with semantic search.',
},
is_correction: {
type: 'boolean',
description: 'Whether this is a correction for a previous learning.',
},
confidence: {
type: 'string',
description: 'How confident you are about this being a correct and useful learning',
enum: ['low' as const, 'medium' as const, 'high' as const],
},
public: {
type: 'boolean',
description:
'Whether this information is specific to the user, or generally applicable to any user of the product',
},
},
required: [
'id' as const,
'text' as const,
'is_correction' as const,
'confidence' as const,
'public' as const,
],
},
},
(
{ arguments: { id, text, is_correction: isCorrection, confidence, public: isPublic } },
signal
) => {
return service
.callApi('POST /internal/observability_ai_assistant/functions/summarise', {
params: {
body: {
id,
text,
is_correction: isCorrection,
confidence,
public: isPublic,
},
},
signal,
})
.then(() => ({
content: {
message: `The document has been stored`,
},
}));
}
);
}
Loading

0 comments on commit 006e1d6

Please sign in to comment.