Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f71a8e4
[Obs AI Assistant] Add KB user instructions
sorenlouv Jul 4, 2024
f54af02
Improve description
sorenlouv Jul 4, 2024
7c65ee9
Address feedback
sorenlouv Jul 5, 2024
4c32316
Revert change to `requestInstructions`
sorenlouv Jul 5, 2024
f18dc24
Merge branch 'main' of github.com:elastic/kibana into kb-user-instruc…
sorenlouv Jul 5, 2024
371fb5e
Merge branch 'main' of github.com:elastic/kibana into kb-user-instruc…
sorenlouv Jul 8, 2024
421386f
Add UI for editing system prompt
sorenlouv Jul 9, 2024
dec0374
Merge branch 'main' into kb-user-instructions
sorenlouv Aug 5, 2024
063575b
Address feedback
sorenlouv Aug 5, 2024
14ec494
Merge branch 'main' of github.com:elastic/kibana into kb-user-instruc…
sorenlouv Aug 7, 2024
b43022b
Add comments
sorenlouv Aug 7, 2024
55255d9
i18n fix
sorenlouv Aug 7, 2024
3fe25f2
Improve naming
sorenlouv Aug 7, 2024
e69c9fb
Rename to adhoc instruction and fix tests
sorenlouv Aug 8, 2024
8a8e87d
Change terminology from “system prompt” to "AI User Profile"
sorenlouv Aug 8, 2024
4e78002
Remove unneeded header
sorenlouv Aug 8, 2024
6c4e406
Fix failing test
sorenlouv Aug 8, 2024
07d7c65
Remove `type`
sorenlouv Aug 9, 2024
9b2b3e1
Add route for retrieving user instructions
sorenlouv Aug 12, 2024
8e1fbcb
Remove unused type
sorenlouv Aug 12, 2024
4a49ee7
Add api tests
sorenlouv Aug 12, 2024
9392a44
Merge branch 'main' of github.com:elastic/kibana into kb-user-instruc…
sorenlouv Aug 13, 2024
88433ef
i18n
sorenlouv Aug 13, 2024
c6892fe
Improve api test
sorenlouv Aug 13, 2024
c05f37c
Fix order
sorenlouv Aug 13, 2024
ab8e19c
Add test for updating instruction
sorenlouv Aug 13, 2024
334d0e7
Test improvements
sorenlouv Aug 13, 2024
299167a
Merge branch 'main' of github.com:elastic/kibana into kb-user-instruc…
sorenlouv Aug 13, 2024
816e6eb
Merge branch 'main' of github.com:elastic/kibana into kb-user-instruc…
sorenlouv Aug 14, 2024
c2953b3
Add test that validates user instruction is added to system prompt
sorenlouv Aug 14, 2024
e433ab7
Add test to verify other users does not see instruction in their conv…
sorenlouv Aug 14, 2024
3639edf
Fik jest test (token count fixes)
sorenlouv Aug 14, 2024
e238ba0
Undo change to tsconfig.base.json
sorenlouv Aug 14, 2024
61bf0f4
Use shared `isFunctionTitleRequest`
sorenlouv Aug 14, 2024
4f960da
Remove unused import
sorenlouv Aug 15, 2024
4f0982c
Fix tsc issues
sorenlouv Aug 15, 2024
55a4fd5
Fix functional test
sorenlouv Aug 15, 2024
d41849c
Fix functional test
sorenlouv Aug 15, 2024
20f56fd
Fix jest
sorenlouv Aug 15, 2024
464b0b8
Remove context intercept
sorenlouv Aug 17, 2024
d76f1ce
Merge branch 'main' into kb-user-instructions
sorenlouv Aug 17, 2024
72f3338
Fix tests by clearing conversations
sorenlouv Aug 18, 2024
3004e2d
Merge branch 'main' into kb-user-instructions
sorenlouv Aug 19, 2024
ed6ee2c
Merge branch 'main' into kb-user-instructions
sorenlouv Aug 19, 2024
ee78461
Merge branch 'main' into kb-user-instructions
sorenlouv Aug 19, 2024
39627f1
Cleanup helper functions
sorenlouv Aug 19, 2024
465b77b
Change to debug
sorenlouv Aug 19, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ export { DEFAULT_LANGUAGE_OPTION, LANGUAGE_OPTIONS } from './ui_settings/languag
export { isSupportedConnectorType } from './connectors';

export { ShortIdTable } from './utils/short_id_table';

export { KnowledgeBaseType } from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export interface KnowledgeBaseEntry {
doc_id: string;
confidence: 'low' | 'medium' | 'high';
is_correction: boolean;
type?: 'user_instruction' | 'contextual';
public: boolean;
labels?: Record<string, string>;
role: KnowledgeBaseEntryRole;
Expand All @@ -92,13 +93,26 @@ export interface KnowledgeBaseEntry {
};
}

export interface UserInstruction {
export interface Instruction {
doc_id: string;
text: string;
system?: boolean;
}

export type UserInstructionOrPlainText = string | UserInstruction;
export interface AdHocInstruction {
doc_id?: string;
text: string;
instruction_type: 'user_instruction' | 'application_instruction';
}

export type InstructionOrPlainText = string | Instruction;

export enum KnowledgeBaseType {
// user instructions are included in the system prompt regardless of the user's input
UserInstruction = 'user_instruction',

// contextual entries are only included in the system prompt if the user's input matches the context
Contextual = 'contextual',
}

export interface ObservabilityAIAssistantScreenContextRequest {
screenDescription?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ export type {
ShortIdTable,
} from '../common';

export { KnowledgeBaseType } from '../common';

export type { TelemetryEventTypeWithPayload } from './analytics';
export { ObservabilityAIAssistantTelemetryEventType } from './analytics/telemetry_event_type';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type {
Message,
ObservabilityAIAssistantScreenContext,
PendingMessage,
UserInstructionOrPlainText,
AdHocInstruction,
} from '../common/types';
import type { TelemetryEventTypeWithPayload } from './analytics';
import type { ObservabilityAIAssistantAPIClient } from './api';
Expand Down Expand Up @@ -68,7 +68,7 @@ export interface ObservabilityAIAssistantChatService {
};
signal: AbortSignal;
responseLanguage?: string;
instructions?: UserInstructionOrPlainText[];
instructions?: AdHocInstruction[];
}) => Observable<StreamingChatResponseEventWithoutError>;
getFunctions: (options?: { contexts?: string[]; filter?: string }) => FunctionDefinition[];
hasFunction: (name: string) => boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { KnowledgeBaseType } from '../../common/types';
import type { FunctionRegistrationParameters } from '.';
import { KnowledgeBaseEntryRole } from '../../common';

Expand Down Expand Up @@ -66,13 +67,14 @@ export function registerSummarizationFunction({
signal
) => {
return client
.createKnowledgeBaseEntry({
.addKnowledgeBaseEntry({
entry: {
doc_id: id,
role: KnowledgeBaseEntryRole.AssistantSummarization,
id,
text,
is_correction: isCorrection,
type: KnowledgeBaseType.Contextual,
confidence,
public: isPublic,
labels: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,15 @@ const chatCompleteBaseRt = t.type({
}),
]),
instructions: t.array(
t.union([
t.string,
t.intersection([
t.type({
doc_id: t.string,
text: t.string,
}),
t.partial({
system: t.boolean,
}),
]),
t.intersection([
t.partial({ doc_id: t.string }),
t.type({
text: t.string,
instruction_type: t.union([
t.literal('user_instruction'),
t.literal('application_instruction'),
]),
}),
])
),
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({
screenContexts: [],
}),
// error is caught in client
client.fetchUserInstructions(),
client.getKnowledgeBaseUserInstructions(),
]);

const functionDefinitions = functionClient.getFunctions().map((fn) => fn.definition);
Expand All @@ -51,9 +51,9 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({
return {
functionDefinitions: functionClient.getFunctions().map((fn) => fn.definition),
systemMessage: getSystemMessageFromInstructions({
registeredInstructions: functionClient.getInstructions(),
applicationInstructions: functionClient.getInstructions(),
userInstructions,
requestInstructions: [],
adHocInstructions: [],
availableFunctionNames,
}),
};
Expand Down Expand Up @@ -111,6 +111,7 @@ const functionSummariseRoute = createObservabilityAIAssistantServerRoute({
text: nonEmptyStringRt,
confidence: t.union([t.literal('low'), t.literal('medium'), t.literal('high')]),
is_correction: toBooleanRt,
type: t.union([t.literal('user_instruction'), t.literal('contextual')]),
public: toBooleanRt,
labels: t.record(t.string, t.string),
}),
Expand All @@ -129,17 +130,19 @@ const functionSummariseRoute = createObservabilityAIAssistantServerRoute({
confidence,
id,
is_correction: isCorrection,
type,
text,
public: isPublic,
labels,
} = resources.params.body;

return client.createKnowledgeBaseEntry({
return client.addKnowledgeBaseEntry({
entry: {
confidence,
id,
doc_id: id,
is_correction: isCorrection,
type,
text,
public: isPublic,
labels,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import { notImplemented } from '@hapi/boom';
import { nonEmptyStringRt, toBooleanRt } from '@kbn/io-ts-utils';
import * as t from 'io-ts';
import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route';
import { KnowledgeBaseEntry, KnowledgeBaseEntryRole } from '../../../common/types';
import {
Instruction,
KnowledgeBaseEntry,
KnowledgeBaseEntryRole,
KnowledgeBaseType,
} from '../../../common/types';

const getKnowledgeBaseStatus = createObservabilityAIAssistantServerRoute({
endpoint: 'GET /internal/observability_ai_assistant/kb/status',
Expand Down Expand Up @@ -60,6 +65,64 @@ const setupKnowledgeBase = createObservabilityAIAssistantServerRoute({
},
});

const getKnowledgeBaseUserInstructions = createObservabilityAIAssistantServerRoute({
endpoint: 'GET /internal/observability_ai_assistant/kb/user_instructions',
options: {
tags: ['access:ai_assistant'],
},
handler: async (
resources
): Promise<{
userInstructions: Array<Instruction & { public?: boolean }>;
}> => {
const client = await resources.service.getClient({ request: resources.request });

if (!client) {
throw notImplemented();
}

return {
userInstructions: await client.getKnowledgeBaseUserInstructions(),
};
},
});

const saveKnowledgeBaseUserInstruction = createObservabilityAIAssistantServerRoute({
endpoint: 'PUT /internal/observability_ai_assistant/kb/user_instructions',
params: t.type({
body: t.type({
id: t.string,
text: nonEmptyStringRt,
public: toBooleanRt,
}),
}),
options: {
tags: ['access:ai_assistant'],
},
handler: async (resources): Promise<void> => {
const client = await resources.service.getClient({ request: resources.request });

if (!client) {
throw notImplemented();
}

const { id, text, public: isPublic } = resources.params.body;
return client.addKnowledgeBaseEntry({
entry: {
id,
doc_id: id,
text,
public: isPublic,
confidence: 'high',
is_correction: false,
type: KnowledgeBaseType.UserInstruction,
labels: {},
role: KnowledgeBaseEntryRole.UserEntry,
},
});
},
});

const getKnowledgeBaseEntries = createObservabilityAIAssistantServerRoute({
endpoint: 'GET /internal/observability_ai_assistant/kb/entries',
options: {
Expand Down Expand Up @@ -130,13 +193,14 @@ const saveKnowledgeBaseEntry = createObservabilityAIAssistantServerRoute({
role,
} = resources.params.body;

return client.createKnowledgeBaseEntry({
return client.addKnowledgeBaseEntry({
entry: {
id,
text,
doc_id: id,
confidence: confidence ?? 'high',
is_correction: isCorrection ?? false,
type: 'contextual',
public: isPublic ?? true,
labels: labels ?? {},
role: (role as KnowledgeBaseEntryRole) ?? KnowledgeBaseEntryRole.UserEntry,
Expand Down Expand Up @@ -192,6 +256,7 @@ const importKnowledgeBaseEntries = createObservabilityAIAssistantServerRoute({
doc_id: entry.id,
confidence: 'high' as KnowledgeBaseEntry['confidence'],
is_correction: false,
type: 'contextual' as const,
public: true,
labels: {},
role: KnowledgeBaseEntryRole.UserEntry,
Expand All @@ -206,6 +271,8 @@ export const knowledgeBaseRoutes = {
...setupKnowledgeBase,
...getKnowledgeBaseStatus,
...getKnowledgeBaseEntries,
...saveKnowledgeBaseUserInstruction,
...getKnowledgeBaseUserInstructions,
...importKnowledgeBaseEntries,
...saveKnowledgeBaseEntry,
...deleteKnowledgeBaseEntry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
FunctionCallChatFunction,
FunctionHandler,
FunctionHandlerRegistry,
RegisteredInstruction,
InstructionOrCallback,
RegisterFunction,
RegisterInstruction,
} from '../types';
Expand All @@ -34,7 +34,7 @@ const ajv = new Ajv({
export const GET_DATA_ON_SCREEN_FUNCTION_NAME = 'get_data_on_screen';

export class ChatFunctionClient {
private readonly instructions: RegisteredInstruction[] = [];
private readonly instructions: InstructionOrCallback[] = [];
private readonly functionRegistry: FunctionHandlerRegistry = new Map();
private readonly validators: Map<string, ValidateFunction> = new Map();

Expand Down Expand Up @@ -107,7 +107,7 @@ export class ChatFunctionClient {
}
}

getInstructions(): RegisteredInstruction[] {
getInstructions(): InstructionOrCallback[] {
return this.instructions;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ import { createFunctionResponseMessage } from '../../../common/utils/create_func
import { CONTEXT_FUNCTION_NAME } from '../../functions/context';
import { ChatFunctionClient } from '../chat_function_client';
import type { KnowledgeBaseService } from '../knowledge_base_service';
import { USER_INSTRUCTIONS_HEADER } from '../util/get_system_message_from_instructions';
import { observableIntoStream } from '../util/observable_into_stream';
import { CreateChatCompletionResponseChunk } from './adapters/process_openai_stream';

type ChunkDelta = CreateChatCompletionResponseChunk['choices'][number]['delta'];

type LlmSimulator = ReturnType<typeof createLlmSimulator>;

const EXPECTED_STORED_SYSTEM_MESSAGE = `system\n\n${USER_INSTRUCTIONS_HEADER}\n\nYou MUST respond in the users preferred language which is: English.`;
const EXPECTED_STORED_SYSTEM_MESSAGE = `system\n\nYou MUST respond in the users preferred language which is: English.`;

const nextTick = () => {
return new Promise(process.nextTick);
Expand Down Expand Up @@ -367,8 +366,8 @@ describe('Observability AI Assistant client', () => {
last_updated: expect.any(String),
token_count: {
completion: 1,
prompt: 84,
total: 85,
prompt: 46,
total: 47,
},
},
type: StreamingChatResponseEventType.ConversationCreate,
Expand Down Expand Up @@ -424,8 +423,8 @@ describe('Observability AI Assistant client', () => {
last_updated: expect.any(String),
token_count: {
completion: 6,
prompt: 268,
total: 274,
prompt: 230,
total: 236,
},
},
type: StreamingChatResponseEventType.ConversationCreate,
Expand All @@ -442,8 +441,8 @@ describe('Observability AI Assistant client', () => {
title: 'An auto-generated title',
token_count: {
completion: 6,
prompt: 268,
total: 274,
prompt: 230,
total: 236,
},
},
labels: {},
Expand Down Expand Up @@ -573,8 +572,8 @@ describe('Observability AI Assistant client', () => {
last_updated: expect.any(String),
token_count: {
completion: 2,
prompt: 162,
total: 164,
prompt: 124,
total: 126,
},
},
type: StreamingChatResponseEventType.ConversationUpdate,
Expand All @@ -592,8 +591,8 @@ describe('Observability AI Assistant client', () => {
title: 'My stored conversation',
token_count: {
completion: 2,
prompt: 162,
total: 164,
prompt: 124,
total: 126,
},
},
labels: {},
Expand Down Expand Up @@ -1609,7 +1608,10 @@ describe('Observability AI Assistant client', () => {
.subscribe(() => {}); // To trigger call to chat
await nextTick();

expect(chatSpy.mock.calls[0][1].messages[0].message.content).toEqual(
const systemMessage = chatSpy.mock.calls[0][1].messages[0];

expect(systemMessage.message.role).toEqual(MessageRole.System);
expect(systemMessage.message.content).toEqual(
EXPECTED_STORED_SYSTEM_MESSAGE.replace('English', 'Orcish')
);
});
Expand Down
Loading