mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.16`: - [[AI Assistant] Set scope and rename to Observability and Search (#196322)](https://github.com/elastic/kibana/pull/196322) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Sander Philipse","email":"94373878+sphilipse@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-24T11:13:43Z","message":"[AI Assistant] Set scope and rename to Observability and Search (#196322)\n\n## Summary\r\n\r\nThis renames the Observability AI Assistant in some places to AI\r\nAssistant for Observability and Search. It also makes the scope\r\nmulti-valued on both sides.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"26ec293f07a990930c4caf9765d79882723dbfa6","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Search","Team:Obs AI Assistant","ci:project-deploy-observability","Team:obs-ux-infra_services","apm:review","v8.16.0","backport:version","v8.17.0"],"title":"[AI Assistant] Set scope and rename to Observability and Search","number":196322,"url":"https://github.com/elastic/kibana/pull/196322","mergeCommit":{"message":"[AI Assistant] Set scope and rename to Observability and Search (#196322)\n\n## Summary\r\n\r\nThis renames the Observability AI Assistant in some places to AI\r\nAssistant for Observability and Search. It also makes the scope\r\nmulti-valued on both sides.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"26ec293f07a990930c4caf9765d79882723dbfa6"}},"sourceBranch":"main","suggestedTargetBranches":["8.16","8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/196322","number":196322,"mergeCommit":{"message":"[AI Assistant] Set scope and rename to Observability and Search (#196322)\n\n## Summary\r\n\r\nThis renames the Observability AI Assistant in some places to AI\r\nAssistant for Observability and Search. It also makes the scope\r\nmulti-valued on both sides.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"26ec293f07a990930c4caf9765d79882723dbfa6"}},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.x","label":"v8.17.0","branchLabelMappingKey":"^v8.17.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Sander Philipse <94373878+sphilipse@users.noreply.github.com>
This commit is contained in:
parent
970267619d
commit
dedfdecbaf
74 changed files with 523 additions and 518 deletions
|
@ -744,7 +744,7 @@ Elastic.
|
|||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/observability_solution/observability_ai_assistant_management/README.md[observabilityAiAssistantManagement]
|
||||
|The observabilityAiAssistantManagement plugin manages the Ai Assistant for Observability management section.
|
||||
|The observabilityAiAssistantManagement plugin manages the Ai Assistant for Observability and Search management section.
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/x-pack/plugins/observability_solution/observability_logs_explorer/README.md[observabilityLogsExplorer]
|
||||
|
|
|
@ -139,7 +139,7 @@ export function AiAssistantSelectionPage() {
|
|||
isDisabled={!observabilityAIAssistantEnabled}
|
||||
title={i18n.translate(
|
||||
'aiAssistantManagementSelection.aiAssistantSelectionPage.observabilityLabel',
|
||||
{ defaultMessage: 'Elastic AI Assistant for Observability' }
|
||||
{ defaultMessage: 'Elastic AI Assistant for Observability and Search' }
|
||||
)}
|
||||
titleSize="xs"
|
||||
/>
|
||||
|
|
|
@ -50,7 +50,7 @@ export class AIAssistantManagementSelectionPlugin
|
|||
core.uiSettings.register({
|
||||
[PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
|
||||
name: i18n.translate('aiAssistantManagementSelection.preferredAIAssistantTypeSettingName', {
|
||||
defaultMessage: 'Observability AI Assistant scope',
|
||||
defaultMessage: 'AI Assistant for Observability and Search visibility',
|
||||
}),
|
||||
category: [DEFAULT_APP_CATEGORIES.observability.id],
|
||||
value: this.config.preferredAIAssistantType,
|
||||
|
@ -58,7 +58,7 @@ export class AIAssistantManagementSelectionPlugin
|
|||
'aiAssistantManagementSelection.preferredAIAssistantTypeSettingDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'<em>[technical preview]</em> Whether to show the Observability AI Assistant menu item in Observability, everywhere, or nowhere.',
|
||||
'<em>[technical preview]</em> Whether to show the AI Assistant menu item in Observability and Search, everywhere, or nowhere.',
|
||||
values: {
|
||||
em: (chunks) => `<em>${chunks}</em>`,
|
||||
},
|
||||
|
@ -77,7 +77,7 @@ export class AIAssistantManagementSelectionPlugin
|
|||
optionLabels: {
|
||||
[AIAssistantType.Default]: i18n.translate(
|
||||
'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueDefault',
|
||||
{ defaultMessage: 'Observability only (default)' }
|
||||
{ defaultMessage: 'Observability and Search only (default)' }
|
||||
),
|
||||
[AIAssistantType.Observability]: i18n.translate(
|
||||
'aiAssistantManagementSelection.preferredAIAssistantTypeSettingValueObservability',
|
||||
|
|
|
@ -7,11 +7,15 @@
|
|||
|
||||
import type { AssistantScope } from '../types';
|
||||
|
||||
export function filterScopes<T extends { scopes?: AssistantScope[] }>(scope?: AssistantScope) {
|
||||
export function filterScopes<T extends { scopes?: AssistantScope[] }>(
|
||||
scopeFilters?: AssistantScope[]
|
||||
) {
|
||||
return function (value: T): boolean {
|
||||
if (!scope || !value) {
|
||||
if (!scopeFilters || !value) {
|
||||
return true;
|
||||
}
|
||||
return value?.scopes ? value.scopes.includes(scope) || value.scopes.includes('all') : true;
|
||||
return value?.scopes
|
||||
? value.scopes.some((scope) => [...scopeFilters, 'all'].includes(scope))
|
||||
: true;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { euiThemeVars } from '@kbn/ui-theme';
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import type { AssistantScope } from '@kbn/ai-assistant-common';
|
||||
import { isEqual } from 'lodash';
|
||||
import { useKibana } from '../hooks/use_kibana';
|
||||
import { ConversationList, ChatBody, ChatInlineEditingContent } from '../chat';
|
||||
import { useConversationKey } from '../hooks/use_conversation_key';
|
||||
|
@ -27,7 +28,7 @@ interface ConversationViewProps {
|
|||
navigateToConversation: (nextConversationId?: string) => void;
|
||||
getConversationHref?: (conversationId: string) => string;
|
||||
newConversationHref?: string;
|
||||
scope?: AssistantScope;
|
||||
scopes?: AssistantScope[];
|
||||
}
|
||||
|
||||
export const ConversationView: React.FC<ConversationViewProps> = ({
|
||||
|
@ -35,7 +36,7 @@ export const ConversationView: React.FC<ConversationViewProps> = ({
|
|||
navigateToConversation,
|
||||
getConversationHref,
|
||||
newConversationHref,
|
||||
scope,
|
||||
scopes,
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
|
@ -61,10 +62,10 @@ export const ConversationView: React.FC<ConversationViewProps> = ({
|
|||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (scope) {
|
||||
service.setScope(scope);
|
||||
if (scopes && !isEqual(scopes, service.getScopes())) {
|
||||
service.setScopes(scopes);
|
||||
}
|
||||
}, [scope, service]);
|
||||
}, [scopes, service]);
|
||||
|
||||
const { key: bodyKey, updateConversationIdInPlace } = useConversationKey(conversationId);
|
||||
|
||||
|
|
|
@ -15,6 +15,6 @@ export function useConversation() {
|
|||
stop: () => {},
|
||||
messages: [],
|
||||
saveTitle: () => {},
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
export * from './use_ai_assistant_app_service';
|
||||
export * from './use_ai_assistant_chat_service';
|
||||
export * from './use_knowledge_base';
|
||||
export * from './use_scope';
|
||||
export * from './use_scopes';
|
||||
|
|
|
@ -55,9 +55,9 @@ const mockService: MockedService = {
|
|||
predefinedConversation$: new Observable(),
|
||||
},
|
||||
navigate: jest.fn().mockReturnValue(of()),
|
||||
scope$: new BehaviorSubject<AssistantScope>('all') as MockedService['scope$'],
|
||||
setScope: jest.fn(),
|
||||
getScope: jest.fn(),
|
||||
scope$: new BehaviorSubject<AssistantScope[]>(['all']) as MockedService['scope$'],
|
||||
setScopes: jest.fn(),
|
||||
getScopes: jest.fn(),
|
||||
};
|
||||
|
||||
const mockChatService = createMockChatService();
|
||||
|
|
|
@ -20,7 +20,7 @@ import { useAIAssistantAppService } from './use_ai_assistant_app_service';
|
|||
import { useKibana } from './use_kibana';
|
||||
import { useOnce } from './use_once';
|
||||
import { useAbortableAsync } from './use_abortable_async';
|
||||
import { useScope } from './use_scope';
|
||||
import { useScopes } from './use_scopes';
|
||||
|
||||
function createNewConversation({
|
||||
title = EMPTY_CONVERSATION_TITLE,
|
||||
|
@ -62,7 +62,7 @@ export function useConversation({
|
|||
onConversationUpdate,
|
||||
}: UseConversationProps): UseConversationResult {
|
||||
const service = useAIAssistantAppService();
|
||||
const scope = useScope();
|
||||
const scopes = useScopes();
|
||||
|
||||
const {
|
||||
services: {
|
||||
|
@ -122,7 +122,7 @@ export function useConversation({
|
|||
onConversationUpdate?.({ conversation: event.conversation });
|
||||
},
|
||||
persist: true,
|
||||
scope,
|
||||
scopes,
|
||||
});
|
||||
|
||||
const [displayedConversationId, setDisplayedConversationId] = useState(initialConversationId);
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import { useObservable } from 'react-use/lib';
|
||||
import { useAIAssistantAppService } from './use_ai_assistant_app_service';
|
||||
|
||||
export const useScope = () => {
|
||||
export const useScopes = () => {
|
||||
const service = useAIAssistantAppService();
|
||||
const scope = useObservable(service.scope$);
|
||||
return scope || 'all';
|
||||
const scopes = useObservable(service.scope$);
|
||||
return scopes || ['all'];
|
||||
};
|
|
@ -32,7 +32,7 @@ export const createMockChatService = (): MockedChatService => {
|
|||
content: '',
|
||||
},
|
||||
}),
|
||||
getScope: jest.fn(),
|
||||
getScopes: jest.fn(),
|
||||
};
|
||||
return mockChatService;
|
||||
};
|
||||
|
|
|
@ -164,7 +164,6 @@ export function registerGetApmDatasetInfoFunction({
|
|||
`,
|
||||
},
|
||||
};
|
||||
},
|
||||
['observability']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ export function registerGetApmDownstreamDependenciesFunction({
|
|||
randomSampler,
|
||||
}),
|
||||
};
|
||||
},
|
||||
['observability']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -84,7 +84,6 @@ export function registerGetApmServicesListFunction({
|
|||
arguments: args,
|
||||
}),
|
||||
};
|
||||
},
|
||||
['observability']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -138,8 +138,7 @@ export function registerGetApmTimeseriesFunction({
|
|||
content: timeseries.map((series): Omit<ApmTimeseries, 'data'> => omit(series, 'data')),
|
||||
data: timeseries,
|
||||
};
|
||||
},
|
||||
['observability']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,10 @@ export function registerAssistantFunctions({
|
|||
ruleDataClient: IRuleDataClient;
|
||||
plugins: APMRouteHandlerResources['plugins'];
|
||||
}): RegistrationCallback {
|
||||
return async ({ resources, functions: { registerFunction } }) => {
|
||||
return async ({ resources, functions: { registerFunction }, scopes }) => {
|
||||
if (!scopes.includes('observability')) {
|
||||
return;
|
||||
}
|
||||
const apmRouteHandlerResources: MinimalAPMRouteHandlerResources = {
|
||||
context: resources.context,
|
||||
request: resources.request,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
import type { JSONSchema7TypeName } from 'json-schema';
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { AssistantScope } from '@kbn/ai-assistant-common';
|
||||
import { ChatCompletionChunkEvent, MessageAddEvent } from '../conversation_complete';
|
||||
import { FunctionVisibility } from './function_visibility';
|
||||
export { FunctionVisibility };
|
||||
|
@ -42,7 +41,6 @@ export interface FunctionDefinition<TParameters extends CompatibleJSONSchema = a
|
|||
visibility?: FunctionVisibility;
|
||||
descriptionForUser?: string;
|
||||
parameters?: TParameters;
|
||||
scopes?: AssistantScope[];
|
||||
}
|
||||
|
||||
export type FunctionRegistry = Map<string, FunctionDefinition>;
|
||||
|
|
|
@ -56,7 +56,7 @@ function ChatContent({
|
|||
}) {
|
||||
const service = useObservabilityAIAssistant();
|
||||
const chatService = useObservabilityAIAssistantChatService();
|
||||
const scope = chatService.getScope();
|
||||
const scopes = chatService.getScopes();
|
||||
|
||||
const initialMessagesRef = useRef(initialMessages);
|
||||
|
||||
|
@ -69,7 +69,7 @@ function ChatContent({
|
|||
initialMessages,
|
||||
persist: false,
|
||||
disableFunctions: true,
|
||||
scope,
|
||||
scopes,
|
||||
});
|
||||
|
||||
const lastAssistantResponse = getLastMessageOfType(
|
||||
|
|
|
@ -39,7 +39,7 @@ const mockChatService: MockedChatService = {
|
|||
role: MessageRole.System,
|
||||
},
|
||||
}),
|
||||
getScope: jest.fn(),
|
||||
getScopes: jest.fn(),
|
||||
};
|
||||
|
||||
const addErrorMock = jest.fn();
|
||||
|
@ -83,7 +83,7 @@ describe('useChat', () => {
|
|||
service: {
|
||||
getScreenContexts: () => [],
|
||||
} as unknown as ObservabilityAIAssistantService,
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
} as UseChatProps,
|
||||
});
|
||||
});
|
||||
|
@ -113,7 +113,7 @@ describe('useChat', () => {
|
|||
service: {
|
||||
getScreenContexts: () => [],
|
||||
} as unknown as ObservabilityAIAssistantService,
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
} as UseChatProps,
|
||||
});
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ interface UseChatPropsWithoutContext {
|
|||
disableFunctions?: boolean;
|
||||
onConversationUpdate?: (event: ConversationCreateEvent | ConversationUpdateEvent) => void;
|
||||
onChatComplete?: (messages: Message[]) => void;
|
||||
scope: AssistantScope;
|
||||
scopes: AssistantScope[];
|
||||
}
|
||||
|
||||
export type UseChatProps = Omit<UseChatPropsWithoutContext, 'notifications'>;
|
||||
|
@ -72,7 +72,7 @@ function useChatWithoutContext({
|
|||
onChatComplete,
|
||||
persist,
|
||||
disableFunctions,
|
||||
scope,
|
||||
scopes,
|
||||
}: UseChatPropsWithoutContext): UseChatResult {
|
||||
const [chatState, setChatState] = useState(ChatState.Ready);
|
||||
const systemMessage = useMemo(() => {
|
||||
|
@ -165,7 +165,7 @@ function useChatWithoutContext({
|
|||
disableFunctions: disableFunctions ?? false,
|
||||
signal: abortControllerRef.current.signal,
|
||||
conversationId,
|
||||
scope,
|
||||
scopes,
|
||||
});
|
||||
|
||||
function getPendingMessages() {
|
||||
|
@ -264,7 +264,7 @@ function useChatWithoutContext({
|
|||
disableFunctions,
|
||||
service,
|
||||
systemMessage,
|
||||
scope,
|
||||
scopes,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ export const mockChatService: ObservabilityAIAssistantChatService = {
|
|||
content: 'System',
|
||||
},
|
||||
}),
|
||||
getScope: jest.fn(),
|
||||
getScopes: jest.fn(),
|
||||
};
|
||||
|
||||
export const mockService: ObservabilityAIAssistantService = {
|
||||
|
@ -64,9 +64,9 @@ export const mockService: ObservabilityAIAssistantService = {
|
|||
predefinedConversation$: new Observable(),
|
||||
},
|
||||
navigate: async () => of(),
|
||||
setScope: jest.fn(),
|
||||
getScope: jest.fn(),
|
||||
scope$: new BehaviorSubject<AssistantScope>('all'),
|
||||
setScopes: jest.fn(),
|
||||
getScopes: jest.fn(),
|
||||
scope$: new BehaviorSubject<AssistantScope[]>(['all']),
|
||||
};
|
||||
|
||||
function createSetupContract(): ObservabilityAIAssistantPublicSetup {
|
||||
|
|
|
@ -67,7 +67,7 @@ export class ObservabilityAIAssistantPlugin
|
|||
coreStart.application.capabilities.observabilityAIAssistant[
|
||||
aiAssistantCapabilities.show
|
||||
] === true,
|
||||
scope: this.scopeFromConfig || 'observability',
|
||||
scopes: this.scopeFromConfig ? [this.scopeFromConfig] : ['all'],
|
||||
scopeIsMutable: !!this.scopeFromConfig,
|
||||
}));
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ describe('complete', () => {
|
|||
disableFunctions: false,
|
||||
signal: new AbortController().signal,
|
||||
...params,
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
},
|
||||
requestCallback
|
||||
);
|
||||
|
|
|
@ -43,7 +43,7 @@ export function complete(
|
|||
disableFunctions,
|
||||
signal,
|
||||
instructions,
|
||||
scope,
|
||||
scopes,
|
||||
}: {
|
||||
client: Pick<ObservabilityAIAssistantChatService, 'chat' | 'complete'>;
|
||||
getScreenContexts: () => ObservabilityAIAssistantScreenContext[];
|
||||
|
@ -66,7 +66,7 @@ export function complete(
|
|||
screenContexts,
|
||||
conversationId,
|
||||
instructions,
|
||||
scope,
|
||||
scopes,
|
||||
},
|
||||
},
|
||||
}).pipe(shareReplay());
|
||||
|
@ -133,7 +133,7 @@ export function complete(
|
|||
persist,
|
||||
disableFunctions,
|
||||
instructions,
|
||||
scope,
|
||||
scopes,
|
||||
},
|
||||
requestCallback
|
||||
).subscribe(subscriber);
|
||||
|
|
|
@ -56,7 +56,7 @@ describe('createChatService', () => {
|
|||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
clientSpy.mockImplementationOnce(async () => {
|
||||
clientSpy.mockImplementation(async () => {
|
||||
return {
|
||||
functionDefinitions: [],
|
||||
contextDefinitions: [],
|
||||
|
@ -71,7 +71,7 @@ describe('createChatService', () => {
|
|||
apiClient: clientSpy,
|
||||
registrations: [],
|
||||
signal: new AbortController().signal,
|
||||
scope$: new BehaviorSubject<AssistantScope>('observability'),
|
||||
scope$: new BehaviorSubject<AssistantScope[]>(['observability']),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -85,7 +85,7 @@ describe('createChatService', () => {
|
|||
signal,
|
||||
messages: [],
|
||||
connectorId: '',
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ class ChatService {
|
|||
private renderFunctionRegistry: Map<string, RenderFunction<unknown, FunctionResponse>>;
|
||||
private abortSignal: AbortSignal;
|
||||
private apiClient: ObservabilityAIAssistantAPIClient;
|
||||
public scope$: BehaviorSubject<AssistantScope>;
|
||||
public scope$: BehaviorSubject<AssistantScope[]>;
|
||||
private analytics: AnalyticsServiceStart;
|
||||
private registrations: ChatRegistrationRenderFunction[];
|
||||
private systemMessage: string;
|
||||
|
@ -159,7 +159,7 @@ class ChatService {
|
|||
}: {
|
||||
abortSignal: AbortSignal;
|
||||
apiClient: ObservabilityAIAssistantAPIClient;
|
||||
scope$: BehaviorSubject<AssistantScope>;
|
||||
scope$: BehaviorSubject<AssistantScope[]>;
|
||||
analytics: AnalyticsServiceStart;
|
||||
registrations: ChatRegistrationRenderFunction[];
|
||||
}) {
|
||||
|
@ -186,15 +186,21 @@ class ChatService {
|
|||
|
||||
async initialize() {
|
||||
this.functionRegistry = new Map();
|
||||
const [{ functionDefinitions, systemMessage }] = await Promise.all([
|
||||
this.apiClient('GET /internal/observability_ai_assistant/{scope}/functions', {
|
||||
signal: this.abortSignal,
|
||||
params: {
|
||||
path: {
|
||||
scope: this.getScope(),
|
||||
},
|
||||
const systemMessages: string[] = [];
|
||||
const scopePromise = this.apiClient('GET /internal/observability_ai_assistant/functions', {
|
||||
signal: this.abortSignal,
|
||||
params: {
|
||||
query: {
|
||||
scopes: this.getScopes(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}).then(({ functionDefinitions, systemMessage }) => {
|
||||
functionDefinitions.forEach((fn) => this.functionRegistry.set(fn.name, fn));
|
||||
systemMessages.push(systemMessage);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
scopePromise,
|
||||
...this.registrations.map((registration) => {
|
||||
return registration({
|
||||
registerRenderFunction: (name, renderFn) => {
|
||||
|
@ -204,10 +210,7 @@ class ChatService {
|
|||
}),
|
||||
]);
|
||||
|
||||
functionDefinitions.forEach((fn) => {
|
||||
this.functionRegistry.set(fn.name, fn);
|
||||
});
|
||||
this.systemMessage = systemMessage;
|
||||
this.systemMessage = systemMessages.join('\n');
|
||||
|
||||
this.functions$.next(this.getFunctions());
|
||||
}
|
||||
|
@ -249,10 +252,6 @@ class ChatService {
|
|||
return filterFunctionDefinitions({
|
||||
...options,
|
||||
definitions: Array.from(this.functionRegistry.values()),
|
||||
}).filter((value) => {
|
||||
return value.scopes
|
||||
? value.scopes?.includes(this.getScope()) || value.scopes?.includes('all')
|
||||
: true;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -301,7 +300,7 @@ class ChatService {
|
|||
connectorId,
|
||||
functionCall,
|
||||
functions: functions ?? [],
|
||||
scope: this.getScope(),
|
||||
scopes: this.getScopes(),
|
||||
},
|
||||
},
|
||||
signal,
|
||||
|
@ -334,7 +333,7 @@ class ChatService {
|
|||
signal,
|
||||
client: this.getClient(),
|
||||
instructions,
|
||||
scope: this.getScope(),
|
||||
scopes: this.getScopes(),
|
||||
},
|
||||
({ params }) => {
|
||||
return this.callStreamingApi('POST /internal/observability_ai_assistant/chat/complete', {
|
||||
|
@ -345,7 +344,7 @@ class ChatService {
|
|||
);
|
||||
};
|
||||
|
||||
public getScope() {
|
||||
public getScopes() {
|
||||
return this.scope$.value;
|
||||
}
|
||||
}
|
||||
|
@ -361,7 +360,7 @@ export async function createChatService({
|
|||
signal: AbortSignal;
|
||||
registrations: ChatRegistrationRenderFunction[];
|
||||
apiClient: ObservabilityAIAssistantAPIClient;
|
||||
scope$: BehaviorSubject<AssistantScope>;
|
||||
scope$: BehaviorSubject<AssistantScope[]>;
|
||||
}): Promise<ObservabilityAIAssistantChatService> {
|
||||
return new ChatService({
|
||||
analytics,
|
||||
|
|
|
@ -29,7 +29,7 @@ export const createMockChatService = (): MockedChatService => {
|
|||
content: 'system',
|
||||
},
|
||||
}),
|
||||
getScope: jest.fn(),
|
||||
getScopes: jest.fn(),
|
||||
};
|
||||
return mockChatService;
|
||||
};
|
||||
|
|
|
@ -20,13 +20,13 @@ export function createService({
|
|||
analytics,
|
||||
coreStart,
|
||||
enabled,
|
||||
scope,
|
||||
scopes,
|
||||
scopeIsMutable,
|
||||
}: {
|
||||
analytics: AnalyticsServiceStart;
|
||||
coreStart: CoreStart;
|
||||
enabled: boolean;
|
||||
scope: AssistantScope;
|
||||
scopes: [AssistantScope];
|
||||
scopeIsMutable: boolean;
|
||||
}): ObservabilityAIAssistantService {
|
||||
const apiClient = createCallObservabilityAIAssistantAPI(coreStart);
|
||||
|
@ -38,13 +38,13 @@ export function createService({
|
|||
]);
|
||||
const predefinedConversation$ = new Subject<{ messages: Message[]; title?: string }>();
|
||||
|
||||
const scope$ = new BehaviorSubject<AssistantScope>(scope);
|
||||
const scope$ = new BehaviorSubject<AssistantScope[]>(scopes);
|
||||
|
||||
const getScreenContexts = () => {
|
||||
const currentScope = scope$.value;
|
||||
const currentScopes = scope$.value;
|
||||
const screenContexts = screenContexts$.value.map(({ starterPrompts, ...rest }) => ({
|
||||
...rest,
|
||||
starterPrompts: starterPrompts?.filter(filterScopes(currentScope)),
|
||||
starterPrompts: starterPrompts?.filter(filterScopes(currentScopes)),
|
||||
}));
|
||||
return screenContexts;
|
||||
};
|
||||
|
@ -58,7 +58,13 @@ export function createService({
|
|||
},
|
||||
start: async ({ signal }) => {
|
||||
const mod = await import('./create_chat_service');
|
||||
return await mod.createChatService({ analytics, apiClient, signal, registrations, scope$ });
|
||||
return await mod.createChatService({
|
||||
analytics,
|
||||
apiClient,
|
||||
signal,
|
||||
registrations,
|
||||
scope$,
|
||||
});
|
||||
},
|
||||
callApi: apiClient,
|
||||
getScreenContexts,
|
||||
|
@ -103,12 +109,12 @@ export function createService({
|
|||
},
|
||||
predefinedConversation$: predefinedConversation$.asObservable(),
|
||||
},
|
||||
setScope: (newScope: AssistantScope) => {
|
||||
setScopes: (newScopes: AssistantScope[]) => {
|
||||
if (!scopeIsMutable) {
|
||||
scope$.next(newScope);
|
||||
scope$.next(newScopes);
|
||||
}
|
||||
},
|
||||
getScope: () => scope$.value,
|
||||
getScopes: () => scope$.value,
|
||||
scope$,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ export const createStorybookChatService = (): ObservabilityAIAssistantChatServic
|
|||
functions$: new BehaviorSubject<FunctionDefinition[]>(
|
||||
[]
|
||||
) as ObservabilityAIAssistantChatService['functions$'],
|
||||
getScope: () => 'all',
|
||||
getScopes: () => ['all'],
|
||||
});
|
||||
|
||||
export const createStorybookService = (): ObservabilityAIAssistantService => ({
|
||||
|
@ -57,7 +57,9 @@ export const createStorybookService = (): ObservabilityAIAssistantService => ({
|
|||
predefinedConversation$: new Observable(),
|
||||
},
|
||||
navigate: async () => of(),
|
||||
scope$: new BehaviorSubject<AssistantScope>('all') as ObservabilityAIAssistantService['scope$'],
|
||||
getScope: () => 'all',
|
||||
setScope: () => {},
|
||||
scope$: new BehaviorSubject<AssistantScope[]>([
|
||||
'all',
|
||||
]) as ObservabilityAIAssistantService['scope$'],
|
||||
getScopes: () => ['all'],
|
||||
setScopes: () => {},
|
||||
});
|
||||
|
|
|
@ -54,7 +54,7 @@ export interface ObservabilityAIAssistantChatService {
|
|||
functions?: Array<Pick<FunctionDefinition, 'name' | 'description' | 'parameters'>>;
|
||||
functionCall?: string;
|
||||
signal: AbortSignal;
|
||||
scope: AssistantScope;
|
||||
scopes: AssistantScope[];
|
||||
}
|
||||
) => Observable<ChatCompletionChunkEvent>;
|
||||
complete: (options: {
|
||||
|
@ -70,12 +70,12 @@ export interface ObservabilityAIAssistantChatService {
|
|||
};
|
||||
signal: AbortSignal;
|
||||
instructions?: AdHocInstruction[];
|
||||
scope: AssistantScope;
|
||||
scopes: AssistantScope[];
|
||||
}) => Observable<StreamingChatResponseEventWithoutError>;
|
||||
getFunctions: (options?: {
|
||||
contexts?: string[];
|
||||
filter?: string;
|
||||
scope: AssistantScope;
|
||||
scopes: AssistantScope[];
|
||||
}) => FunctionDefinition[];
|
||||
functions$: BehaviorSubject<FunctionDefinition[]>;
|
||||
hasFunction: (name: string) => boolean;
|
||||
|
@ -87,7 +87,7 @@ export interface ObservabilityAIAssistantChatService {
|
|||
response: { data?: string; content?: string },
|
||||
onActionClick: ChatActionClickHandler
|
||||
) => React.ReactNode;
|
||||
getScope: () => AssistantScope;
|
||||
getScopes: () => AssistantScope[];
|
||||
}
|
||||
|
||||
export interface ObservabilityAIAssistantConversationService {
|
||||
|
@ -104,9 +104,9 @@ export interface ObservabilityAIAssistantService {
|
|||
getScreenContexts: () => ObservabilityAIAssistantScreenContext[];
|
||||
conversations: ObservabilityAIAssistantConversationService;
|
||||
navigate: (callback: () => void) => Promise<Observable<MessageAddEvent>>;
|
||||
scope$: BehaviorSubject<AssistantScope>;
|
||||
setScope: (scope: AssistantScope) => void;
|
||||
getScope: () => AssistantScope;
|
||||
scope$: BehaviorSubject<AssistantScope[]>;
|
||||
setScopes: (scope: AssistantScope[]) => void;
|
||||
getScopes: () => AssistantScope[];
|
||||
}
|
||||
|
||||
export type RenderFunction<TArguments, TResponse extends FunctionResponse> = (options: {
|
||||
|
|
|
@ -115,7 +115,6 @@ export function registerContextFunction({
|
|||
subscriber.complete();
|
||||
});
|
||||
});
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ export function registerElasticsearchFunction({
|
|||
});
|
||||
|
||||
return { content: { response } };
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ export function registerExecuteConnectorFunction({
|
|||
).getActionsClientWithRequest(resources.request);
|
||||
const content = await actionsClient.execute({ actionId: id, params });
|
||||
return { content };
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -94,7 +94,6 @@ export function registerGetDatasetInfoFunction({
|
|||
stats: relevantFieldNames.stats,
|
||||
},
|
||||
};
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -28,20 +28,47 @@ export const registerFunctions: RegistrationCallback = async ({
|
|||
functions,
|
||||
resources,
|
||||
signal,
|
||||
scopes,
|
||||
}) => {
|
||||
const registrationParameters: FunctionRegistrationParameters = {
|
||||
client,
|
||||
functions,
|
||||
resources,
|
||||
signal,
|
||||
scopes,
|
||||
};
|
||||
|
||||
const isServerless = !!resources.plugins.serverless;
|
||||
if (scopes.includes('observability')) {
|
||||
functions.registerInstruction(`You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.
|
||||
|
||||
functions.registerInstruction({
|
||||
instruction: `You are a helpful assistant for Elastic Observability. Your goal is to help the Elastic Observability users to quickly assess what is happening in their observed systems. You can help them visualise and analyze data, investigate their systems, perform root cause analysis or identify optimisation opportunities.
|
||||
It's very important to not assume what the user is meaning. Ask them for clarification if needed.
|
||||
|
||||
It's very important to not assume what the user is meaning. Ask them for clarification if needed.
|
||||
If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.
|
||||
|
||||
In KQL ("kqlFilter")) escaping happens with double quotes, not single quotes. Some characters that need escaping are: ':()\\\
|
||||
/\". Always put a field value in double quotes. Best: service.name:\"opbeans-go\". Wrong: service.name:opbeans-go. This is very important!
|
||||
|
||||
You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.
|
||||
|
||||
Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.
|
||||
|
||||
If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results
|
||||
returned to you, before executing the same tool or another tool again if needed.
|
||||
|
||||
DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (\`service.name == "foo"\`) with "kqlFilter" (\`service.name:"foo"\`).
|
||||
|
||||
The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability and Search, which can be found in the ${
|
||||
isServerless ? `Project settings.` : `Stack Management app under the option AI Assistants`
|
||||
}.
|
||||
If the user asks how to change the language, reply in the same language the user asked in.`);
|
||||
}
|
||||
|
||||
if (scopes.length === 0 || (scopes.length === 1 && scopes[0] === 'all')) {
|
||||
functions.registerInstruction(
|
||||
`You are a helpful assistant for Elasticsearch. Your goal is to help Elasticsearch users accomplish tasks using Kibana and Elasticsearch. You can help them construct queries, index data, search data, use Elasticsearch APIs, generate sample data, visualise and analyze data.
|
||||
|
||||
It's very important to not assume what the user means. Ask them for clarification if needed.
|
||||
|
||||
If you are unsure about which function should be used and with what arguments, ask the user for clarification or confirmation.
|
||||
|
||||
|
@ -50,63 +77,56 @@ export const registerFunctions: RegistrationCallback = async ({
|
|||
|
||||
You can use Github-flavored Markdown in your responses. If a function returns an array, consider using a Markdown table to format the response.
|
||||
|
||||
Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.
|
||||
|
||||
If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results
|
||||
returned to you, before executing the same tool or another tool again if needed.
|
||||
|
||||
DO NOT UNDER ANY CIRCUMSTANCES USE ES|QL syntax (\`service.name == "foo"\`) with "kqlFilter" (\`service.name:"foo"\`).
|
||||
|
||||
The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the ${
|
||||
The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability and Search, which can be found in the ${
|
||||
isServerless ? `Project settings.` : `Stack Management app under the option AI Assistants`
|
||||
}.
|
||||
If the user asks how to change the language, reply in the same language the user asked in.`,
|
||||
scopes: ['observability'],
|
||||
});
|
||||
If the user asks how to change the language, reply in the same language the user asked in.`
|
||||
);
|
||||
}
|
||||
|
||||
const { ready: isReady } = await client.getKnowledgeBaseStatus();
|
||||
|
||||
functions.registerInstruction({
|
||||
instruction: ({ availableFunctionNames }) => {
|
||||
const instructions: string[] = [];
|
||||
functions.registerInstruction(({ availableFunctionNames }) => {
|
||||
const instructions: string[] = [];
|
||||
|
||||
if (
|
||||
availableFunctionNames.includes(QUERY_FUNCTION_NAME) &&
|
||||
availableFunctionNames.includes(GET_DATASET_INFO_FUNCTION_NAME)
|
||||
) {
|
||||
instructions.push(`You MUST use the "${GET_DATASET_INFO_FUNCTION_NAME}" ${
|
||||
functions.hasFunction('get_apm_dataset_info') ? 'or the get_apm_dataset_info' : ''
|
||||
} function before calling the "${QUERY_FUNCTION_NAME}" or the "changes" functions.
|
||||
if (
|
||||
availableFunctionNames.includes(QUERY_FUNCTION_NAME) &&
|
||||
availableFunctionNames.includes(GET_DATASET_INFO_FUNCTION_NAME)
|
||||
) {
|
||||
instructions.push(`You MUST use the "${GET_DATASET_INFO_FUNCTION_NAME}" ${
|
||||
functions.hasFunction('get_apm_dataset_info') ? 'or the get_apm_dataset_info' : ''
|
||||
} function before calling the "${QUERY_FUNCTION_NAME}" or the "changes" functions.
|
||||
|
||||
If a function requires an index, you MUST use the results from the dataset info functions.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (availableFunctionNames.includes(GET_DATA_ON_SCREEN_FUNCTION_NAME)) {
|
||||
instructions.push(`You have access to data on the screen by calling the "${GET_DATA_ON_SCREEN_FUNCTION_NAME}" function.
|
||||
if (availableFunctionNames.includes(GET_DATA_ON_SCREEN_FUNCTION_NAME)) {
|
||||
instructions.push(`You have access to data on the screen by calling the "${GET_DATA_ON_SCREEN_FUNCTION_NAME}" function.
|
||||
Use it to help the user understand what they are looking at. A short summary of what they are looking at is available in the return of the "${CONTEXT_FUNCTION_NAME}" function.
|
||||
Data that is compact enough automatically gets included in the response for the "${CONTEXT_FUNCTION_NAME}" function.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isReady) {
|
||||
if (availableFunctionNames.includes(SUMMARIZE_FUNCTION_NAME)) {
|
||||
instructions.push(`You can use the "${SUMMARIZE_FUNCTION_NAME}" function to store new information you have learned in a knowledge database.
|
||||
if (isReady) {
|
||||
if (availableFunctionNames.includes(SUMMARIZE_FUNCTION_NAME)) {
|
||||
instructions.push(`You can use the "${SUMMARIZE_FUNCTION_NAME}" function to store new information you have learned in a knowledge database.
|
||||
Only use this function when the user asks for it.
|
||||
All summaries MUST be created in English, even if the conversation was carried out in a different language.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (availableFunctionNames.includes(CONTEXT_FUNCTION_NAME)) {
|
||||
instructions.push(
|
||||
`Additionally, you can use the "${CONTEXT_FUNCTION_NAME}" function to retrieve relevant information from the knowledge database.`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (availableFunctionNames.includes(CONTEXT_FUNCTION_NAME)) {
|
||||
instructions.push(
|
||||
`You do not have a working memory. If the user expects you to remember the previous conversations, tell them they can set up the knowledge base.`
|
||||
`Additionally, you can use the "${CONTEXT_FUNCTION_NAME}" function to retrieve relevant information from the knowledge database.`
|
||||
);
|
||||
}
|
||||
return instructions.map((instruction) => dedent(instruction));
|
||||
},
|
||||
scopes: ['all'],
|
||||
} else {
|
||||
instructions.push(
|
||||
`You do not have a working memory. If the user expects you to remember the previous conversations, tell them they can set up the knowledge base.`
|
||||
);
|
||||
}
|
||||
return instructions.map((instruction) => dedent(instruction));
|
||||
});
|
||||
|
||||
if (isReady) {
|
||||
|
|
|
@ -95,7 +95,6 @@ export function registerKibanaFunction({
|
|||
}).then((response) => {
|
||||
return { content: response.data };
|
||||
});
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -86,7 +86,6 @@ export function registerSummarizationFunction({
|
|||
message: `The document has been stored`,
|
||||
},
|
||||
}));
|
||||
},
|
||||
['observability']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ const chatCompleteInternalRt = t.intersection([
|
|||
t.type({
|
||||
body: t.type({
|
||||
screenContexts: t.array(screenContextRt),
|
||||
scope: assistantScopeType,
|
||||
scopes: t.array(assistantScopeType),
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
|
@ -83,11 +83,11 @@ async function initializeChatRequest({
|
|||
request,
|
||||
plugins: { cloud, actions },
|
||||
params: {
|
||||
body: { connectorId, scope },
|
||||
body: { connectorId, scopes },
|
||||
},
|
||||
service,
|
||||
}: ObservabilityAIAssistantRouteHandlerResources & {
|
||||
params: { body: { connectorId: string; scope: AssistantScope } };
|
||||
params: { body: { connectorId: string; scopes: AssistantScope[] } };
|
||||
}) {
|
||||
await withAssistantSpan('guard_against_invalid_connector', async () => {
|
||||
const actionsClient = await (await actions.start()).getActionsClientWithRequest(request);
|
||||
|
@ -101,7 +101,7 @@ async function initializeChatRequest({
|
|||
});
|
||||
|
||||
const [client, cloudStart, simulateFunctionCalling] = await Promise.all([
|
||||
service.getClient({ request, scope }),
|
||||
service.getClient({ request, scopes }),
|
||||
cloud?.start(),
|
||||
(await context.core).uiSettings.client.get<boolean>(aiAssistantSimulatedFunctionCalling),
|
||||
]);
|
||||
|
@ -136,7 +136,7 @@ const chatRoute = createObservabilityAIAssistantServerRoute({
|
|||
messages: t.array(messageRt),
|
||||
connectorId: t.string,
|
||||
functions: t.array(functionRt),
|
||||
scope: assistantScopeType,
|
||||
scopes: t.array(assistantScopeType),
|
||||
}),
|
||||
t.partial({
|
||||
functionCall: t.string,
|
||||
|
@ -182,7 +182,7 @@ const chatRecallRoute = createObservabilityAIAssistantServerRoute({
|
|||
prompt: t.string,
|
||||
context: t.string,
|
||||
connectorId: t.string,
|
||||
scope: assistantScopeType,
|
||||
scopes: t.array(assistantScopeType),
|
||||
}),
|
||||
}),
|
||||
handler: async (resources): Promise<Readable> => {
|
||||
|
@ -248,6 +248,7 @@ async function chatComplete(
|
|||
screenContexts,
|
||||
instructions,
|
||||
disableFunctions,
|
||||
scopes,
|
||||
},
|
||||
} = params;
|
||||
|
||||
|
@ -260,6 +261,7 @@ async function chatComplete(
|
|||
resources,
|
||||
client,
|
||||
screenContexts,
|
||||
scopes,
|
||||
});
|
||||
|
||||
const response$ = client.complete({
|
||||
|
@ -310,7 +312,7 @@ const publicChatCompleteRoute = createObservabilityAIAssistantServerRoute({
|
|||
params: {
|
||||
body: {
|
||||
...restOfBody,
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
screenContexts: [
|
||||
{
|
||||
actions,
|
||||
|
|
|
@ -15,10 +15,10 @@ import { createObservabilityAIAssistantServerRoute } from '../create_observabili
|
|||
import { assistantScopeType } from '../runtime_types';
|
||||
|
||||
const getFunctionsRoute = createObservabilityAIAssistantServerRoute({
|
||||
endpoint: 'GET /internal/observability_ai_assistant/{scope}/functions',
|
||||
endpoint: 'GET /internal/observability_ai_assistant/functions',
|
||||
params: t.type({
|
||||
path: t.type({
|
||||
scope: assistantScopeType,
|
||||
query: t.partial({
|
||||
scopes: t.union([t.array(assistantScopeType), assistantScopeType]),
|
||||
}),
|
||||
}),
|
||||
options: {
|
||||
|
@ -34,10 +34,12 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({
|
|||
service,
|
||||
request,
|
||||
params: {
|
||||
path: { scope },
|
||||
query: { scopes: inputScopes },
|
||||
},
|
||||
} = resources;
|
||||
|
||||
const scopes = inputScopes ? (Array.isArray(inputScopes) ? inputScopes : [inputScopes]) : [];
|
||||
|
||||
const controller = new AbortController();
|
||||
request.events.aborted$.subscribe(() => {
|
||||
controller.abort();
|
||||
|
@ -51,19 +53,20 @@ const getFunctionsRoute = createObservabilityAIAssistantServerRoute({
|
|||
resources,
|
||||
client,
|
||||
screenContexts: [],
|
||||
scopes,
|
||||
}),
|
||||
// error is caught in client
|
||||
client.getKnowledgeBaseUserInstructions(),
|
||||
]);
|
||||
|
||||
const functionDefinitions = functionClient.getFunctions({ scope }).map((fn) => fn.definition);
|
||||
const functionDefinitions = functionClient.getFunctions().map((fn) => fn.definition);
|
||||
|
||||
const availableFunctionNames = functionDefinitions.map((def) => def.name);
|
||||
|
||||
return {
|
||||
functionDefinitions,
|
||||
systemMessage: getSystemMessageFromInstructions({
|
||||
applicationInstructions: functionClient.getInstructions(scope),
|
||||
applicationInstructions: functionClient.getInstructions(),
|
||||
userInstructions,
|
||||
adHocInstructions: [],
|
||||
availableFunctionNames,
|
||||
|
|
|
@ -34,8 +34,7 @@ describe('chatFunctionClient', () => {
|
|||
required: ['foo'],
|
||||
},
|
||||
},
|
||||
respondFn,
|
||||
['all']
|
||||
respondFn
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import Ajv, { type ErrorObject, type ValidateFunction } from 'ajv';
|
||||
import dedent from 'dedent';
|
||||
import { compact, keyBy } from 'lodash';
|
||||
import { type AssistantScope, filterScopes } from '@kbn/ai-assistant-common';
|
||||
import { FunctionVisibility, type FunctionResponse } from '../../../common/functions/types';
|
||||
import type { Message, ObservabilityAIAssistantScreenContextRequest } from '../../../common/types';
|
||||
import { filterFunctionDefinitions } from '../../../common/utils/filter_function_definitions';
|
||||
|
@ -18,7 +17,6 @@ import type {
|
|||
FunctionHandler,
|
||||
FunctionHandlerRegistry,
|
||||
InstructionOrCallback,
|
||||
InstructionOrCallbackWithScopes,
|
||||
RegisterFunction,
|
||||
RegisterInstruction,
|
||||
} from '../types';
|
||||
|
@ -36,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: InstructionOrCallbackWithScopes[] = [];
|
||||
private readonly instructions: InstructionOrCallback[] = [];
|
||||
private readonly functionRegistry: FunctionHandlerRegistry = new Map();
|
||||
private readonly validators: Map<string, ValidateFunction> = new Map();
|
||||
|
||||
|
@ -75,8 +73,7 @@ export class ChatFunctionClient {
|
|||
return {
|
||||
content: allData.filter((data) => dataNames.includes(data.name)),
|
||||
};
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -87,11 +84,11 @@ export class ChatFunctionClient {
|
|||
});
|
||||
}
|
||||
|
||||
registerFunction: RegisterFunction = (definition, respond, scopes) => {
|
||||
registerFunction: RegisterFunction = (definition, respond) => {
|
||||
if (definition.parameters) {
|
||||
this.validators.set(definition.name, ajv.compile(definition.parameters));
|
||||
}
|
||||
this.functionRegistry.set(definition.name, { handler: { definition, respond }, scopes });
|
||||
this.functionRegistry.set(definition.name, { handler: { definition, respond } });
|
||||
};
|
||||
|
||||
registerInstruction: RegisterInstruction = (instruction) => {
|
||||
|
@ -110,8 +107,8 @@ export class ChatFunctionClient {
|
|||
}
|
||||
}
|
||||
|
||||
getInstructions(scope: AssistantScope): InstructionOrCallback[] {
|
||||
return this.instructions.filter(filterScopes(scope)).map((i) => i.instruction);
|
||||
getInstructions(): InstructionOrCallback[] {
|
||||
return this.instructions;
|
||||
}
|
||||
|
||||
hasAction(name: string) {
|
||||
|
@ -120,14 +117,10 @@ export class ChatFunctionClient {
|
|||
|
||||
getFunctions({
|
||||
filter,
|
||||
scope,
|
||||
}: {
|
||||
filter?: string;
|
||||
scope?: AssistantScope;
|
||||
} = {}): FunctionHandler[] {
|
||||
const allFunctions = Array.from(this.functionRegistry.values())
|
||||
.filter(filterScopes(scope))
|
||||
.map(({ handler }) => handler);
|
||||
const allFunctions = Array.from(this.functionRegistry.values()).map(({ handler }) => handler);
|
||||
|
||||
const functionsByName = keyBy(allFunctions, (definition) => definition.definition.name);
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ describe('Observability AI Assistant client', () => {
|
|||
user: {
|
||||
name: 'johndoe',
|
||||
},
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ export class ObservabilityAIAssistantClient {
|
|||
name: string;
|
||||
};
|
||||
knowledgeBaseService: KnowledgeBaseService;
|
||||
scope: AssistantScope;
|
||||
scopes: AssistantScope[];
|
||||
}
|
||||
) {}
|
||||
|
||||
|
@ -217,11 +217,11 @@ export class ObservabilityAIAssistantClient {
|
|||
// this is what we eventually store in the conversation
|
||||
const messagesWithUpdatedSystemMessage = replaceSystemMessage(
|
||||
getSystemMessageFromInstructions({
|
||||
applicationInstructions: functionClient.getInstructions(this.dependencies.scope),
|
||||
applicationInstructions: functionClient.getInstructions(),
|
||||
userInstructions,
|
||||
adHocInstructions,
|
||||
availableFunctionNames: functionClient
|
||||
.getFunctions({ scope: this.dependencies.scope })
|
||||
.getFunctions()
|
||||
.map((fn) => fn.definition.name),
|
||||
}),
|
||||
initialMessages
|
||||
|
@ -301,7 +301,6 @@ export class ObservabilityAIAssistantClient {
|
|||
disableFunctions,
|
||||
tracer: completeTracer,
|
||||
connectorId,
|
||||
scope: this.dependencies.scope,
|
||||
useSimulatedFunctionCalling: simulateFunctionCalling === true,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -21,7 +21,6 @@ import {
|
|||
switchMap,
|
||||
throwError,
|
||||
} from 'rxjs';
|
||||
import type { AssistantScope } from '@kbn/ai-assistant-common';
|
||||
import { CONTEXT_FUNCTION_NAME } from '../../../functions/context';
|
||||
import { createFunctionNotFoundError, Message, MessageRole } from '../../../../common';
|
||||
import {
|
||||
|
@ -138,7 +137,6 @@ function getFunctionDefinitions({
|
|||
functionClient,
|
||||
functionLimitExceeded,
|
||||
disableFunctions,
|
||||
scope,
|
||||
}: {
|
||||
functionClient: ChatFunctionClient;
|
||||
functionLimitExceeded: boolean;
|
||||
|
@ -147,14 +145,13 @@ function getFunctionDefinitions({
|
|||
| {
|
||||
except: string[];
|
||||
};
|
||||
scope: AssistantScope;
|
||||
}) {
|
||||
if (functionLimitExceeded || disableFunctions === true) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let systemFunctions = functionClient
|
||||
.getFunctions({ scope })
|
||||
.getFunctions()
|
||||
.map((fn) => fn.definition)
|
||||
.filter(
|
||||
(def) =>
|
||||
|
@ -187,7 +184,6 @@ export function continueConversation({
|
|||
disableFunctions,
|
||||
tracer,
|
||||
connectorId,
|
||||
scope,
|
||||
useSimulatedFunctionCalling,
|
||||
}: {
|
||||
messages: Message[];
|
||||
|
@ -205,7 +201,6 @@ export function continueConversation({
|
|||
};
|
||||
tracer: LangTracer;
|
||||
connectorId: string;
|
||||
scope: AssistantScope;
|
||||
useSimulatedFunctionCalling: boolean;
|
||||
}): Observable<MessageOrChatEvent> {
|
||||
let nextFunctionCallsLeft = functionCallsLeft;
|
||||
|
@ -216,12 +211,11 @@ export function continueConversation({
|
|||
functionLimitExceeded,
|
||||
functionClient,
|
||||
disableFunctions,
|
||||
scope,
|
||||
});
|
||||
|
||||
const messagesWithUpdatedSystemMessage = replaceSystemMessage(
|
||||
getSystemMessageFromInstructions({
|
||||
applicationInstructions: functionClient.getInstructions(scope),
|
||||
applicationInstructions: functionClient.getInstructions(),
|
||||
userInstructions,
|
||||
adHocInstructions,
|
||||
availableFunctionNames: definitions.map((def) => def.name),
|
||||
|
@ -350,7 +344,6 @@ export function continueConversation({
|
|||
disableFunctions,
|
||||
tracer,
|
||||
connectorId,
|
||||
scope,
|
||||
useSimulatedFunctionCalling,
|
||||
});
|
||||
})
|
||||
|
|
|
@ -249,10 +249,10 @@ export class ObservabilityAIAssistantService {
|
|||
|
||||
async getClient({
|
||||
request,
|
||||
scope,
|
||||
scopes,
|
||||
}: {
|
||||
request: KibanaRequest;
|
||||
scope?: AssistantScope;
|
||||
scopes?: AssistantScope[];
|
||||
}): Promise<ObservabilityAIAssistantClient> {
|
||||
const controller = new AbortController();
|
||||
|
||||
|
@ -291,7 +291,7 @@ export class ObservabilityAIAssistantService {
|
|||
}
|
||||
: undefined,
|
||||
knowledgeBaseService: this.kbService!,
|
||||
scope: scope || 'all',
|
||||
scopes: scopes || ['all'],
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -300,11 +300,13 @@ export class ObservabilityAIAssistantService {
|
|||
signal,
|
||||
resources,
|
||||
client,
|
||||
scopes,
|
||||
}: {
|
||||
screenContexts: ObservabilityAIAssistantScreenContextRequest[];
|
||||
signal: AbortSignal;
|
||||
resources: RespondFunctionResources;
|
||||
client: ObservabilityAIAssistantClient;
|
||||
scopes: AssistantScope[];
|
||||
}): Promise<ChatFunctionClient> {
|
||||
const fnClient = new ChatFunctionClient(screenContexts);
|
||||
|
||||
|
@ -313,6 +315,7 @@ export class ObservabilityAIAssistantService {
|
|||
functions: fnClient,
|
||||
resources,
|
||||
client,
|
||||
scopes,
|
||||
};
|
||||
|
||||
await Promise.all(
|
||||
|
|
|
@ -68,18 +68,13 @@ export interface FunctionHandler {
|
|||
|
||||
export type InstructionOrCallback = InstructionOrPlainText | RegisterInstructionCallback;
|
||||
|
||||
export interface InstructionOrCallbackWithScopes {
|
||||
instruction: InstructionOrCallback;
|
||||
scopes: AssistantScope[];
|
||||
}
|
||||
|
||||
export type RegisterInstructionCallback = ({
|
||||
availableFunctionNames,
|
||||
}: {
|
||||
availableFunctionNames: string[];
|
||||
}) => InstructionOrPlainText | InstructionOrPlainText[] | undefined;
|
||||
|
||||
export type RegisterInstruction = (...instruction: InstructionOrCallbackWithScopes[]) => void;
|
||||
export type RegisterInstruction = (...instruction: InstructionOrCallback[]) => void;
|
||||
|
||||
export type RegisterFunction = <
|
||||
TParameters extends CompatibleJSONSchema = any,
|
||||
|
@ -87,17 +82,14 @@ export type RegisterFunction = <
|
|||
TArguments = FromSchema<TParameters>
|
||||
>(
|
||||
definition: FunctionDefinition<TParameters>,
|
||||
respond: RespondFunction<TArguments, TResponse>,
|
||||
scopes: AssistantScope[]
|
||||
respond: RespondFunction<TArguments, TResponse>
|
||||
) => void;
|
||||
export type FunctionHandlerRegistry = Map<
|
||||
string,
|
||||
{ handler: FunctionHandler; scopes: AssistantScope[] }
|
||||
>;
|
||||
export type FunctionHandlerRegistry = Map<string, { handler: FunctionHandler }>;
|
||||
|
||||
export type RegistrationCallback = ({}: {
|
||||
signal: AbortSignal;
|
||||
resources: RespondFunctionResources;
|
||||
client: ObservabilityAIAssistantClient;
|
||||
functions: ChatFunctionClient;
|
||||
scopes: AssistantScope[];
|
||||
}) => Promise<void>;
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
"@kbn/core-ui-settings-server",
|
||||
"@kbn/inference-plugin",
|
||||
"@kbn/management-settings-ids",
|
||||
"@kbn/ai-assistant-common"
|
||||
"@kbn/ai-assistant-common",
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ import { useAIAssistantAppService } from '@kbn/ai-assistant';
|
|||
import { AssistantScope } from '@kbn/ai-assistant-common';
|
||||
import { useObservable } from 'react-use/lib';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
|
||||
import { isEqual } from 'lodash';
|
||||
import { useKibana } from './use_kibana';
|
||||
|
||||
const scopeUrlLookup: Record<string, AssistantScope> = {
|
||||
[DEFAULT_APP_CATEGORIES.observability.id]: 'observability',
|
||||
[DEFAULT_APP_CATEGORIES.enterpriseSearch.id]: 'search',
|
||||
const scopeUrlLookup: Record<string, AssistantScope[]> = {
|
||||
[DEFAULT_APP_CATEGORIES.observability.id]: ['observability'],
|
||||
[DEFAULT_APP_CATEGORIES.enterpriseSearch.id]: ['search'],
|
||||
};
|
||||
|
||||
export function useNavControlScope() {
|
||||
|
@ -31,11 +32,9 @@ export function useNavControlScope() {
|
|||
const currentCategoryId =
|
||||
(currentApplication && applications?.get(currentApplication)?.category?.id) ||
|
||||
DEFAULT_APP_CATEGORIES.kibana.id;
|
||||
const newScope = Object.entries(scopeUrlLookup).find(
|
||||
([categoryId]) => categoryId === currentCategoryId
|
||||
)?.[1];
|
||||
if (newScope && newScope !== service.getScope()) {
|
||||
service.setScope(newScope);
|
||||
const newScopes = scopeUrlLookup[currentCategoryId];
|
||||
if (newScopes?.length && !isEqual(service.getScopes(), newScopes)) {
|
||||
service.setScopes(newScopes);
|
||||
}
|
||||
}, [applications, currentApplication, service]);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ export function ConversationViewWithProps() {
|
|||
},
|
||||
})
|
||||
}
|
||||
scopes={['observability']}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ function runEvaluations() {
|
|||
evaluationConnectorId: evaluationConnector.id!,
|
||||
persist: argv.persist,
|
||||
suite: mocha.suite,
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
});
|
||||
|
||||
const header: string[][] = [
|
||||
|
|
|
@ -239,13 +239,13 @@ export class KibanaClient {
|
|||
evaluationConnectorId,
|
||||
persist,
|
||||
suite,
|
||||
scope,
|
||||
scopes,
|
||||
}: {
|
||||
connectorId: string;
|
||||
evaluationConnectorId: string;
|
||||
persist: boolean;
|
||||
suite?: Mocha.Suite;
|
||||
scope: AssistantScope;
|
||||
scopes: AssistantScope[];
|
||||
}): ChatClient {
|
||||
function getMessages(message: string | Array<Message['message']>): Array<Message['message']> {
|
||||
if (typeof message === 'string') {
|
||||
|
@ -373,7 +373,7 @@ export class KibanaClient {
|
|||
connectorId: connectorIdOverride || connectorId,
|
||||
functions: functions.map((fn) => pick(fn, 'name', 'description', 'parameters')),
|
||||
functionCall,
|
||||
scope,
|
||||
scopes,
|
||||
};
|
||||
|
||||
return that.axios.post(
|
||||
|
@ -463,7 +463,7 @@ export class KibanaClient {
|
|||
connectorId,
|
||||
persist,
|
||||
title: currentTitle,
|
||||
scope,
|
||||
scopes,
|
||||
},
|
||||
{ responseType: 'stream', timeout: NaN }
|
||||
)
|
||||
|
|
|
@ -74,154 +74,155 @@ export function registerAlertsFunction({
|
|||
functions,
|
||||
resources,
|
||||
pluginsStart,
|
||||
scopes,
|
||||
}: FunctionRegistrationParameters) {
|
||||
functions.registerFunction(
|
||||
{
|
||||
name: 'get_alerts_dataset_info',
|
||||
visibility: FunctionVisibility.AssistantOnly,
|
||||
description: `Use this function to get information about alerts data.`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
start: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The start of the current time range, in datemath, like now-24h or an ISO timestamp',
|
||||
if (scopes.includes('observability')) {
|
||||
functions.registerFunction(
|
||||
{
|
||||
name: 'get_alerts_dataset_info',
|
||||
visibility: FunctionVisibility.AssistantOnly,
|
||||
description: `Use this function to get information about alerts data.`,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
start: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The start of the current time range, in datemath, like now-24h or an ISO timestamp',
|
||||
},
|
||||
end: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The end of the current time range, in datemath, like now-24h or an ISO timestamp',
|
||||
},
|
||||
},
|
||||
end: {
|
||||
type: 'string',
|
||||
description:
|
||||
'The end of the current time range, in datemath, like now-24h or an ISO timestamp',
|
||||
},
|
||||
},
|
||||
} as const,
|
||||
},
|
||||
async (
|
||||
{ arguments: { start, end }, chat, messages },
|
||||
signal
|
||||
): Promise<{
|
||||
content: {
|
||||
fields: string[];
|
||||
};
|
||||
}> => {
|
||||
const core = await resources.context.core;
|
||||
|
||||
const { fields } = await getRelevantFieldNames({
|
||||
index: `.alerts-observability*`,
|
||||
messages,
|
||||
esClient: core.elasticsearch.client.asInternalUser,
|
||||
dataViews: await resources.plugins.dataViews.start(),
|
||||
savedObjectsClient: core.savedObjects.client,
|
||||
signal,
|
||||
chat: (
|
||||
operationName,
|
||||
{ messages: nextMessages, functionCall, functions: nextFunctions }
|
||||
) => {
|
||||
return chat(operationName, {
|
||||
messages: nextMessages,
|
||||
functionCall,
|
||||
functions: nextFunctions,
|
||||
signal,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
} as const,
|
||||
},
|
||||
async (
|
||||
{ arguments: { start, end }, chat, messages },
|
||||
signal
|
||||
): Promise<{
|
||||
content: {
|
||||
fields: fields.length === 0 ? defaultFields : fields,
|
||||
},
|
||||
};
|
||||
},
|
||||
['observability']
|
||||
);
|
||||
fields: string[];
|
||||
};
|
||||
}> => {
|
||||
const core = await resources.context.core;
|
||||
|
||||
functions.registerFunction(
|
||||
{
|
||||
name: 'alerts',
|
||||
description: `Get alerts for Observability. Make sure get_alerts_dataset_info was called before.
|
||||
const { fields } = await getRelevantFieldNames({
|
||||
index: `.alerts-observability*`,
|
||||
messages,
|
||||
esClient: core.elasticsearch.client.asInternalUser,
|
||||
dataViews: await resources.plugins.dataViews.start(),
|
||||
savedObjectsClient: core.savedObjects.client,
|
||||
signal,
|
||||
chat: (
|
||||
operationName,
|
||||
{ messages: nextMessages, functionCall, functions: nextFunctions }
|
||||
) => {
|
||||
return chat(operationName, {
|
||||
messages: nextMessages,
|
||||
functionCall,
|
||||
functions: nextFunctions,
|
||||
signal,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
content: {
|
||||
fields: fields.length === 0 ? defaultFields : fields,
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
functions.registerFunction(
|
||||
{
|
||||
name: 'alerts',
|
||||
description: `Get alerts for Observability. Make sure get_alerts_dataset_info was called before.
|
||||
Use this to get open (and optionally recovered) alerts for Observability assets, like services,
|
||||
hosts or containers.
|
||||
Display the response in tabular format if appropriate.
|
||||
`,
|
||||
descriptionForUser: 'Get alerts for Observability',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
start: {
|
||||
type: 'string',
|
||||
description: 'The start of the time range, in Elasticsearch date math, like `now`.',
|
||||
descriptionForUser: 'Get alerts for Observability',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
start: {
|
||||
type: 'string',
|
||||
description: 'The start of the time range, in Elasticsearch date math, like `now`.',
|
||||
},
|
||||
end: {
|
||||
type: 'string',
|
||||
description: 'The end of the time range, in Elasticsearch date math, like `now-24h`.',
|
||||
},
|
||||
kqlFilter: {
|
||||
type: 'string',
|
||||
description: `Filter alerts by field:value pairs`,
|
||||
},
|
||||
includeRecovered: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned',
|
||||
},
|
||||
},
|
||||
end: {
|
||||
type: 'string',
|
||||
description: 'The end of the time range, in Elasticsearch date math, like `now-24h`.',
|
||||
},
|
||||
kqlFilter: {
|
||||
type: 'string',
|
||||
description: `Filter alerts by field:value pairs`,
|
||||
},
|
||||
includeRecovered: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether to include recovered/closed alerts. Defaults to false, which means only active alerts will be returned',
|
||||
},
|
||||
},
|
||||
required: ['start', 'end'],
|
||||
} as const,
|
||||
},
|
||||
async (
|
||||
{ arguments: { start: startAsDatemath, end: endAsDatemath, filter, includeRecovered } },
|
||||
signal
|
||||
) => {
|
||||
const alertsClient = await pluginsStart.ruleRegistry.getRacClientWithRequest(
|
||||
resources.request as KibanaRequest
|
||||
);
|
||||
required: ['start', 'end'],
|
||||
} as const,
|
||||
},
|
||||
async (
|
||||
{ arguments: { start: startAsDatemath, end: endAsDatemath, filter, includeRecovered } },
|
||||
signal
|
||||
) => {
|
||||
const alertsClient = await pluginsStart.ruleRegistry.getRacClientWithRequest(
|
||||
resources.request as KibanaRequest
|
||||
);
|
||||
|
||||
const start = datemath.parse(startAsDatemath)!.valueOf();
|
||||
const end = datemath.parse(endAsDatemath)!.valueOf();
|
||||
const start = datemath.parse(startAsDatemath)!.valueOf();
|
||||
const end = datemath.parse(endAsDatemath)!.valueOf();
|
||||
|
||||
const kqlQuery = !filter ? [] : [toElasticsearchQuery(fromKueryExpression(filter))];
|
||||
const kqlQuery = !filter ? [] : [toElasticsearchQuery(fromKueryExpression(filter))];
|
||||
|
||||
const response = await alertsClient.find({
|
||||
featureIds: DEFAULT_FEATURE_IDS as unknown as string[],
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: start,
|
||||
lte: end,
|
||||
const response = await alertsClient.find({
|
||||
featureIds: DEFAULT_FEATURE_IDS as unknown as string[],
|
||||
query: {
|
||||
bool: {
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: start,
|
||||
lte: end,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
...kqlQuery,
|
||||
...(!includeRecovered
|
||||
? [
|
||||
{
|
||||
term: {
|
||||
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
|
||||
...kqlQuery,
|
||||
...(!includeRecovered
|
||||
? [
|
||||
{
|
||||
term: {
|
||||
[ALERT_STATUS]: ALERT_STATUS_ACTIVE,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
size: 10,
|
||||
});
|
||||
size: 10,
|
||||
});
|
||||
|
||||
// trim some fields
|
||||
const alerts = response.hits.hits.map((hit) =>
|
||||
omit(hit._source, ...OMITTED_ALERT_FIELDS)
|
||||
) as unknown as ParsedTechnicalFields[];
|
||||
// trim some fields
|
||||
const alerts = response.hits.hits.map((hit) =>
|
||||
omit(hit._source, ...OMITTED_ALERT_FIELDS)
|
||||
) as unknown as ParsedTechnicalFields[];
|
||||
|
||||
return {
|
||||
content: {
|
||||
total: (response.hits as { total: { value: number } }).total.value,
|
||||
alerts,
|
||||
},
|
||||
};
|
||||
},
|
||||
['observability']
|
||||
);
|
||||
return {
|
||||
content: {
|
||||
total: (response.hits as { total: { value: number } }).total.value,
|
||||
alerts,
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,131 +25,133 @@ export function registerChangesFunction({
|
|||
context: { core: corePromise },
|
||||
},
|
||||
pluginsStart,
|
||||
scopes,
|
||||
}: FunctionRegistrationParameters) {
|
||||
functions.registerFunction(
|
||||
{
|
||||
name: CHANGES_FUNCTION_NAME,
|
||||
description: 'Returns change points like spikes and dips for logs and metrics.',
|
||||
parameters: changesFunctionParameters,
|
||||
},
|
||||
async ({
|
||||
arguments: { start, end, logs = [], metrics = [] },
|
||||
}): Promise<ChangesFunctionResponse> => {
|
||||
if (logs.length === 0 && metrics.length === 0) {
|
||||
throw new Error('No metrics or logs were defined');
|
||||
}
|
||||
if (scopes.includes('observability')) {
|
||||
functions.registerFunction(
|
||||
{
|
||||
name: CHANGES_FUNCTION_NAME,
|
||||
description: 'Returns change points like spikes and dips for logs and metrics.',
|
||||
parameters: changesFunctionParameters,
|
||||
},
|
||||
async ({
|
||||
arguments: { start, end, logs = [], metrics = [] },
|
||||
}): Promise<ChangesFunctionResponse> => {
|
||||
if (logs.length === 0 && metrics.length === 0) {
|
||||
throw new Error('No metrics or logs were defined');
|
||||
}
|
||||
|
||||
const core = await corePromise;
|
||||
const core = await corePromise;
|
||||
|
||||
const logSourcesService =
|
||||
await pluginsStart.logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService(
|
||||
core.savedObjects.client
|
||||
);
|
||||
const logsIndexPattern = await logSourcesService.getFlattenedLogSources();
|
||||
const logSourcesService =
|
||||
await pluginsStart.logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService(
|
||||
core.savedObjects.client
|
||||
);
|
||||
const logsIndexPattern = await logSourcesService.getFlattenedLogSources();
|
||||
|
||||
const client = createElasticsearchClient({
|
||||
client: core.elasticsearch.client.asCurrentUser,
|
||||
logger,
|
||||
inspect: logger.isLevelEnabled('debug'),
|
||||
});
|
||||
const client = createElasticsearchClient({
|
||||
client: core.elasticsearch.client.asCurrentUser,
|
||||
logger,
|
||||
inspect: logger.isLevelEnabled('debug'),
|
||||
});
|
||||
|
||||
const commonFilters = [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: start,
|
||||
lt: end,
|
||||
const commonFilters = [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: start,
|
||||
lt: end,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
];
|
||||
|
||||
const dateHistogram: AggregationsAutoDateHistogramAggregation = {
|
||||
field: '@timestamp',
|
||||
buckets: 100,
|
||||
};
|
||||
const dateHistogram: AggregationsAutoDateHistogramAggregation = {
|
||||
field: '@timestamp',
|
||||
buckets: 100,
|
||||
};
|
||||
|
||||
const [metricChanges, logChanges] = await Promise.all([
|
||||
Promise.all([
|
||||
...metrics.map(async (metric) => {
|
||||
const changes = await getMetricChanges({
|
||||
index: metric.index,
|
||||
client,
|
||||
filters: [
|
||||
...commonFilters,
|
||||
...(metric.kqlFilter
|
||||
? [toElasticsearchQuery(fromKueryExpression(metric.kqlFilter))]
|
||||
: []),
|
||||
],
|
||||
groupBy: metric.groupBy ?? [],
|
||||
type: metric.type || 'count',
|
||||
field: metric.field,
|
||||
dateHistogram,
|
||||
});
|
||||
const [metricChanges, logChanges] = await Promise.all([
|
||||
Promise.all([
|
||||
...metrics.map(async (metric) => {
|
||||
const changes = await getMetricChanges({
|
||||
index: metric.index,
|
||||
client,
|
||||
filters: [
|
||||
...commonFilters,
|
||||
...(metric.kqlFilter
|
||||
? [toElasticsearchQuery(fromKueryExpression(metric.kqlFilter))]
|
||||
: []),
|
||||
],
|
||||
groupBy: metric.groupBy ?? [],
|
||||
type: metric.type || 'count',
|
||||
field: metric.field,
|
||||
dateHistogram,
|
||||
});
|
||||
|
||||
return changes.map((change) => ({
|
||||
name: metric.name,
|
||||
...change,
|
||||
}));
|
||||
}),
|
||||
]),
|
||||
Promise.all([
|
||||
...logs.map(async (log) => {
|
||||
const changes = await getLogChanges({
|
||||
index: log.index || logsIndexPattern,
|
||||
client,
|
||||
filters: [
|
||||
...commonFilters,
|
||||
...(log.kqlFilter
|
||||
? [toElasticsearchQuery(fromKueryExpression(log.kqlFilter))]
|
||||
: []),
|
||||
],
|
||||
field: log.field ?? 'message',
|
||||
dateHistogram,
|
||||
});
|
||||
return changes.map((change) => ({
|
||||
name: log.name,
|
||||
...change,
|
||||
}));
|
||||
}),
|
||||
]),
|
||||
]);
|
||||
return changes.map((change) => ({
|
||||
name: metric.name,
|
||||
...change,
|
||||
}));
|
||||
}),
|
||||
]),
|
||||
Promise.all([
|
||||
...logs.map(async (log) => {
|
||||
const changes = await getLogChanges({
|
||||
index: log.index || logsIndexPattern,
|
||||
client,
|
||||
filters: [
|
||||
...commonFilters,
|
||||
...(log.kqlFilter
|
||||
? [toElasticsearchQuery(fromKueryExpression(log.kqlFilter))]
|
||||
: []),
|
||||
],
|
||||
field: log.field ?? 'message',
|
||||
dateHistogram,
|
||||
});
|
||||
return changes.map((change) => ({
|
||||
name: log.name,
|
||||
...change,
|
||||
}));
|
||||
}),
|
||||
]),
|
||||
]);
|
||||
|
||||
const allMetricChanges = orderBy(metricChanges.flat(), [
|
||||
(item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY),
|
||||
]).slice(0, 25);
|
||||
const allMetricChanges = orderBy(metricChanges.flat(), [
|
||||
(item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY),
|
||||
]).slice(0, 25);
|
||||
|
||||
const allMetricChangesWithoutTimeseries = allMetricChanges.flat().map((metricChange) => {
|
||||
return omit(metricChange, 'over_time');
|
||||
});
|
||||
const allMetricChangesWithoutTimeseries = allMetricChanges.flat().map((metricChange) => {
|
||||
return omit(metricChange, 'over_time');
|
||||
});
|
||||
|
||||
const allLogChanges = orderBy(logChanges.flat(), [
|
||||
(item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY),
|
||||
]).slice(0, 25);
|
||||
const allLogChanges = orderBy(logChanges.flat(), [
|
||||
(item) => ('p_value' in item.changes ? item.changes.p_value : Number.POSITIVE_INFINITY),
|
||||
]).slice(0, 25);
|
||||
|
||||
const allLogChangesWithoutTimeseries = allLogChanges.flat().map((logChange) => {
|
||||
return omit(logChange, 'over_time');
|
||||
});
|
||||
const allLogChangesWithoutTimeseries = allLogChanges.flat().map((logChange) => {
|
||||
return omit(logChange, 'over_time');
|
||||
});
|
||||
|
||||
return {
|
||||
content: {
|
||||
description: `For each item, the user can see the type of change, the impact, the timestamp, the trend, and the label.
|
||||
return {
|
||||
content: {
|
||||
description: `For each item, the user can see the type of change, the impact, the timestamp, the trend, and the label.
|
||||
Do not regurgitate these results back to the user.
|
||||
Instead, focus on the interesting changes, mention possible correlations or root causes, and suggest next steps to the user.
|
||||
"indeterminate" means that the system could not detect any changes.`,
|
||||
changes: {
|
||||
metrics: allMetricChangesWithoutTimeseries,
|
||||
logs: allLogChangesWithoutTimeseries,
|
||||
changes: {
|
||||
metrics: allMetricChangesWithoutTimeseries,
|
||||
logs: allLogChangesWithoutTimeseries,
|
||||
},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
changes: {
|
||||
metrics: allMetricChanges,
|
||||
logs: allLogChanges,
|
||||
data: {
|
||||
changes: {
|
||||
metrics: allMetricChanges,
|
||||
logs: allLogChanges,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
['observability']
|
||||
);
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,9 @@ import type { ChatFunctionClient } from '@kbn/observability-ai-assistant-plugin/
|
|||
import { lensFunctionDefinition } from '../../common/functions/lens';
|
||||
|
||||
export function registerLensFunction({ functions }: { functions: ChatFunctionClient }) {
|
||||
functions.registerFunction(
|
||||
lensFunctionDefinition,
|
||||
async () => {
|
||||
return {
|
||||
content: {},
|
||||
};
|
||||
},
|
||||
['all']
|
||||
);
|
||||
functions.registerFunction(lensFunctionDefinition, async () => {
|
||||
return {
|
||||
content: {},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export function registerQueryFunction({
|
|||
When the "visualize_query" function has been called, a visualization has been displayed to the user. DO NOT UNDER ANY CIRCUMSTANCES follow up a "visualize_query" function call with your own visualization attempt.
|
||||
If the "${EXECUTE_QUERY_NAME}" function has been called, summarize these results for the user. The user does not see a visualization in this case.`
|
||||
: undefined;
|
||||
functions.registerInstruction({ instruction, scopes: ['all'] });
|
||||
functions.registerInstruction(instruction);
|
||||
|
||||
functions.registerFunction(
|
||||
{
|
||||
|
@ -103,8 +103,7 @@ export function registerQueryFunction({
|
|||
rows,
|
||||
},
|
||||
};
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
functions.registerFunction(
|
||||
{
|
||||
|
@ -188,7 +187,6 @@ export function registerQueryFunction({
|
|||
return messageAddEvent;
|
||||
})
|
||||
);
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@ export function registerVisualizeESQLFunction({
|
|||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
['all']
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -154,12 +154,13 @@ async function executor(
|
|||
}
|
||||
|
||||
const resources = await initResources(request);
|
||||
const client = await resources.service.getClient({ request, scope: 'observability' });
|
||||
const client = await resources.service.getClient({ request, scopes: ['observability'] });
|
||||
const functionClient = await resources.service.getFunctionClient({
|
||||
signal: new AbortController().signal,
|
||||
resources,
|
||||
client,
|
||||
screenContexts: [],
|
||||
scopes: ['observability'],
|
||||
});
|
||||
const actionsClient = await (
|
||||
await resources.plugins.actions.start()
|
||||
|
@ -227,7 +228,7 @@ If available, include the link of the conversation at the end of your answer.`
|
|||
role: MessageRole.System,
|
||||
content: getSystemMessageFromInstructions({
|
||||
availableFunctionNames: functionClient.getFunctions().map((fn) => fn.definition.name),
|
||||
applicationInstructions: functionClient.getInstructions('observability'),
|
||||
applicationInstructions: functionClient.getInstructions(),
|
||||
userInstructions: [],
|
||||
adHocInstructions: [],
|
||||
}),
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# `observabilityAiAssistantManagement` plugin
|
||||
|
||||
The `observabilityAiAssistantManagement` plugin manages the `Ai Assistant for Observability` management section.
|
||||
The `observabilityAiAssistantManagement` plugin manages the `Ai Assistant for Observability and Search` management section.
|
||||
|
|
|
@ -35,7 +35,7 @@ export const mountManagementSection = async ({ core, mountParams }: MountParams)
|
|||
|
||||
coreStart.chrome.docTitle.change(
|
||||
i18n.translate('xpack.observabilityAiAssistantManagement.app.titleBar', {
|
||||
defaultMessage: 'AI Assistant for Observability Settings',
|
||||
defaultMessage: 'AI Assistant for Observability and Search Settings',
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ export class AiAssistantManagementObservabilityPlugin
|
|||
{ home, management, observabilityAIAssistant }: SetupDependencies
|
||||
): AiAssistantManagementObservabilityPluginSetup {
|
||||
const title = i18n.translate('xpack.observabilityAiAssistantManagement.app.title', {
|
||||
defaultMessage: 'AI Assistant for Observability',
|
||||
defaultMessage: 'AI Assistant for Observability and Search',
|
||||
});
|
||||
|
||||
if (home) {
|
||||
|
@ -57,7 +57,7 @@ export class AiAssistantManagementObservabilityPlugin
|
|||
id: 'ai_assistant_observability',
|
||||
title,
|
||||
description: i18n.translate('xpack.observabilityAiAssistantManagement.app.description', {
|
||||
defaultMessage: 'Manage your AI Assistant for Observability.',
|
||||
defaultMessage: 'Manage your AI Assistant for Observability and Search.',
|
||||
}),
|
||||
icon: 'sparkles',
|
||||
path: '/app/management/kibana/ai-assistant/observability',
|
||||
|
|
|
@ -40,7 +40,7 @@ export function SettingsPage() {
|
|||
text: i18n.translate(
|
||||
'xpack.observabilityAiAssistantManagement.breadcrumb.serverless.observability',
|
||||
{
|
||||
defaultMessage: 'AI Assistant for Observability Settings',
|
||||
defaultMessage: 'AI Assistant for Observability and Search Settings',
|
||||
}
|
||||
),
|
||||
},
|
||||
|
|
|
@ -85,7 +85,7 @@ export function SettingsTab() {
|
|||
'xpack.observabilityAiAssistantManagement.settingsPage.euiDescribedFormGroup.inOrderToUseLabel',
|
||||
{
|
||||
defaultMessage:
|
||||
'In order to use the Observability AI Assistant you must set up a Generative AI connector.',
|
||||
'In order to use the AI Assistant you must set up a Generative AI connector.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
"@kbn/observability-shared-plugin",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/core-ui-settings-common",
|
||||
"@kbn/logs-data-access-plugin"
|
||||
"@kbn/logs-data-access-plugin",
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export function ConversationViewWithProps() {
|
|||
getConversationHref={(id: string) =>
|
||||
http?.basePath.prepend(`/app/searchAssistant/conversations/${id || ''}`) || ''
|
||||
}
|
||||
scope="search"
|
||||
scopes={['search']}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,10 @@ import { RegistrationCallback } from '@kbn/observability-ai-assistant-plugin/ser
|
|||
|
||||
export const registerFunctions: (isServerless: boolean) => RegistrationCallback =
|
||||
(isServerless: boolean) =>
|
||||
async ({ client, functions, resources, signal }) => {
|
||||
functions.registerInstruction({
|
||||
instruction: `You are a helpful assistant for Elasticsearch. Your goal is to help Elasticsearch users accomplish tasks using Kibana and Elasticsearch. You can help them construct queries, index data, search data, use Elasticsearch APIs, generate sample data, visualise and analyze data.
|
||||
async ({ client, functions, resources, signal, scopes }) => {
|
||||
if (scopes.includes('search')) {
|
||||
functions.registerInstruction(
|
||||
`You are a helpful assistant for Elasticsearch. Your goal is to help Elasticsearch users accomplish tasks using Kibana and Elasticsearch. You can help them construct queries, index data, search data, use Elasticsearch APIs, generate sample data, visualise and analyze data.
|
||||
|
||||
It's very important to not assume what the user means. Ask them for clarification if needed.
|
||||
|
||||
|
@ -27,10 +28,10 @@ export const registerFunctions: (isServerless: boolean) => RegistrationCallback
|
|||
If you want to call a function or tool, only call it a single time per message. Wait until the function has been executed and its results
|
||||
returned to you, before executing the same tool or another tool again if needed.
|
||||
|
||||
The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability, which can be found in the ${
|
||||
The user is able to change the language which they want you to reply in on the settings page of the AI Assistant for Observability and Search, which can be found in the ${
|
||||
isServerless ? `Project settings.` : `Stack Management app under the option AI Assistants`
|
||||
}.
|
||||
If the user asks how to change the language, reply in the same language the user asked in.`,
|
||||
scopes: ['search'],
|
||||
});
|
||||
If the user asks how to change the language, reply in the same language the user asked in.`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ export class ServerlessObservabilityPlugin
|
|||
observabilityAiAssistantManagement: {
|
||||
category: appCategories.OTHER,
|
||||
title: i18n.translate('xpack.serverlessObservability.aiAssistantManagementTitle', {
|
||||
defaultMessage: 'AI Assistant for Observability Settings',
|
||||
defaultMessage: 'AI Assistant for Observability and Search Settings',
|
||||
}),
|
||||
description: i18n.translate(
|
||||
'xpack.serverlessObservability.aiAssistantManagementDescription',
|
||||
|
|
|
@ -59,7 +59,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
messages,
|
||||
connectorId: 'does not exist',
|
||||
functions: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.expect(404);
|
||||
});
|
||||
|
@ -88,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
messages,
|
||||
connectorId,
|
||||
functions: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.pipe(passThrough);
|
||||
|
||||
|
@ -146,7 +146,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
messages,
|
||||
connectorId,
|
||||
functions: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.expect(200)
|
||||
.pipe(passThrough);
|
||||
|
|
|
@ -84,7 +84,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: true,
|
||||
screenContexts: params.screenContexts || [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.then((response) => resolve(response))
|
||||
.catch((err) => reject(err));
|
||||
|
@ -137,7 +137,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: false,
|
||||
screenContexts: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.pipe(passThrough);
|
||||
|
||||
|
@ -404,7 +404,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: true,
|
||||
screenContexts: [],
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -447,7 +447,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
persist: true,
|
||||
screenContexts: [],
|
||||
conversationId,
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -33,14 +33,14 @@ export async function invokeChatCompleteWithFunctionRequest({
|
|||
connectorId,
|
||||
observabilityAIAssistantAPIClient,
|
||||
functionCall,
|
||||
scope,
|
||||
scopes,
|
||||
}: {
|
||||
connectorId: string;
|
||||
observabilityAIAssistantAPIClient: Awaited<
|
||||
ReturnType<CreateTest['services']['observabilityAIAssistantAPIClient']>
|
||||
>;
|
||||
functionCall: Message['message']['function_call'];
|
||||
scope?: AssistantScope;
|
||||
scopes?: AssistantScope[];
|
||||
}) {
|
||||
const { body } = await observabilityAIAssistantAPIClient
|
||||
.editorUser({
|
||||
|
@ -60,7 +60,7 @@ export async function invokeChatCompleteWithFunctionRequest({
|
|||
connectorId,
|
||||
persist: false,
|
||||
screenContexts: [],
|
||||
scope: scope || 'observability',
|
||||
scopes: scopes || ['observability' as AssistantScope],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -249,7 +249,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: true,
|
||||
screenContexts: [],
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
},
|
||||
},
|
||||
}).expect(200);
|
||||
|
|
|
@ -84,7 +84,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
messages,
|
||||
connectorId: 'does not exist',
|
||||
functions: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.expect(404);
|
||||
});
|
||||
|
@ -114,7 +114,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
messages,
|
||||
connectorId,
|
||||
functions: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.pipe(passThrough);
|
||||
|
||||
|
@ -174,7 +174,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
messages,
|
||||
connectorId,
|
||||
functions: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.expect(200)
|
||||
.pipe(passThrough);
|
||||
|
|
|
@ -91,7 +91,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: true,
|
||||
screenContexts: params.screenContexts || [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.then((response: Response) => resolve(response))
|
||||
.catch((err: Error) => reject(err));
|
||||
|
@ -164,7 +164,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: false,
|
||||
screenContexts: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
})
|
||||
.pipe(passThrough);
|
||||
|
||||
|
@ -436,7 +436,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: true,
|
||||
screenContexts: [],
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -483,7 +483,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
persist: true,
|
||||
screenContexts: [],
|
||||
conversationId,
|
||||
scope: 'all',
|
||||
scopes: ['all'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -36,12 +36,12 @@ export async function invokeChatCompleteWithFunctionRequest({
|
|||
functionCall,
|
||||
roleAuthc,
|
||||
internalReqHeader,
|
||||
scope,
|
||||
scopes,
|
||||
}: {
|
||||
connectorId: string;
|
||||
observabilityAIAssistantAPIClient: ObservabilityAIAssistantApiClient;
|
||||
functionCall: Message['message']['function_call'];
|
||||
scope?: AssistantScope;
|
||||
scopes?: AssistantScope[];
|
||||
roleAuthc: RoleCredentials;
|
||||
internalReqHeader: InternalRequestHeader;
|
||||
}) {
|
||||
|
@ -65,7 +65,7 @@ export async function invokeChatCompleteWithFunctionRequest({
|
|||
connectorId,
|
||||
persist: false,
|
||||
screenContexts: [],
|
||||
scope: 'observability',
|
||||
scopes: scopes || (['observability'] as AssistantScope[]),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -266,7 +266,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
connectorId,
|
||||
persist: true,
|
||||
screenContexts: [],
|
||||
scope: 'observability',
|
||||
scopes: ['observability'],
|
||||
},
|
||||
},
|
||||
roleAuthc,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue