Skip to content

Commit dc019f8

Browse files
viduni94Søren Louv-Jansenkibanamachinesorenlouv
authored
[Obs AI Assistant] Specify embedding model during onboarding for the Knowledge Base (elastic#218448)
Closes elastic/obs-ai-team#230 Closes elastic/obs-ai-team#232 Related to elastic#215591 ## Summary This PR implements the changes related to the first phase of supporing multilingual Knowledge Base. The users have the ability to pick the `e5-small` model for the Knowledge Base, if they want support for languages other than English. <img width="610" alt="image" src="https://github.com/user-attachments/assets/4c815aa4-aa97-4845-98c5-e079dd92f23a" /> <img width="1281" alt="image" src="https://github.com/user-attachments/assets/7c1bcd82-5464-497f-a053-7fe271da1cdd" /> <img width="1280" alt="image" src="https://github.com/user-attachments/assets/bc084e90-c291-44ea-8560-e033729bfcca" /> When the KB model is not allocated due to nodes scaling down: ![image](https://github.com/user-attachments/assets/2f52e31e-81e4-4824-bc5b-b97df714da5c) ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ## Upgrade testing steps ### 9.0 - 9.1 (main) Checkout `9.0` branch and start Kibana and ES. ES must be started with `path.data` to persist data: ``` yarn es snapshot --license trial --E path.data=/Users/sorenlouv/elastic/es_data/upgrade_test_9.0 ``` --------- Co-authored-by: Søren Louv-Jansen <soren.louv@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Søren Louv-Jansen <sorenlouv@gmail.com>
1 parent 1069221 commit dc019f8

169 files changed

Lines changed: 4264 additions & 2058 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/kbn-lock-manager/src/lock_manager_client.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,19 @@ export class LockManager {
256256
}
257257
}
258258

259+
export async function getLock({
260+
esClient,
261+
logger,
262+
lockId,
263+
}: {
264+
esClient: ElasticsearchClient;
265+
logger: Logger;
266+
lockId: LockId;
267+
}): Promise<LockDocument | undefined> {
268+
const lockManager = new LockManager(lockId, esClient, logger);
269+
return lockManager.get();
270+
}
271+
259272
export async function withLock<T>(
260273
{
261274
esClient,
@@ -280,9 +293,7 @@ export async function withLock<T>(
280293

281294
// extend the ttl periodically
282295
const extendInterval = Math.floor(ttl / 4);
283-
logger.debug(
284-
`Lock "${lockId}" acquired. Extending TTL every ${prettyMilliseconds(extendInterval)}`
285-
);
296+
logger.debug(`Extending TTL for lock "${lockId}" every ${prettyMilliseconds(extendInterval)}`);
286297

287298
let extendTTlPromise = Promise.resolve(true);
288299
const intervalId = setInterval(() => {

packages/kbn-lock-manager/src/lock_manager_service.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import { CoreSetup, Logger } from '@kbn/core/server';
11-
import { LockId, withLock } from './lock_manager_client';
11+
import { LockId, withLock, getLock } from './lock_manager_client';
1212

1313
export class LockManagerService {
1414
constructor(private readonly coreSetup: CoreSetup<any>, private readonly logger: Logger) {}
@@ -35,8 +35,16 @@ export class LockManagerService {
3535
) {
3636
const [coreStart] = await this.coreSetup.getStartServices();
3737
const esClient = coreStart.elasticsearch.client.asInternalUser;
38-
const logger = this.logger.get('LockManager');
38+
const logger = this.logger.get('lock-manager');
3939

4040
return withLock<T>({ esClient, logger, lockId, metadata }, callback);
4141
}
42+
43+
async getLock(lockId: LockId) {
44+
const [coreStart] = await this.coreSetup.getStartServices();
45+
const esClient = coreStart.elasticsearch.client.asInternalUser;
46+
const logger = this.logger.get('lock-manager');
47+
48+
return getLock({ esClient, logger, lockId });
49+
}
4250
}

src/dev/run_check_file_casing.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ run(async ({ log }) => {
3434
'src/platform/**',
3535
'x-pack/platform/**',
3636
'x-pack/solutions/**',
37+
38+
// ignore autogenerated snapshots
39+
'x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/knowledge_base/snapshots',
3740
],
3841
});
3942

x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/welcome_message.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@ import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public';
1212
import { GenerativeAIForObservabilityConnectorFeatureId } from '@kbn/actions-plugin/common';
1313
import { isSupportedConnectorType } from '@kbn/inference-common';
1414
import { AssistantBeacon } from '@kbn/ai-assistant-icon';
15-
import { KnowledgeBaseState } from '@kbn/observability-ai-assistant-plugin/public';
1615
import type { UseKnowledgeBaseResult } from '../hooks/use_knowledge_base';
1716
import type { UseGenAIConnectorsResult } from '../hooks/use_genai_connectors';
1817
import { Disclaimer } from './disclaimer';
1918
import { WelcomeMessageConnectors } from './welcome_message_connectors';
20-
import { WelcomeMessageKnowledgeBase } from './welcome_message_knowledge_base';
19+
import { WelcomeMessageKnowledgeBase } from '../knowledge_base/welcome_message_knowledge_base';
2120
import { StarterPrompts } from './starter_prompts';
2221
import { useKibana } from '../hooks/use_kibana';
2322

@@ -61,13 +60,6 @@ export function WelcomeMessage({
6160
if (isSupportedConnectorType(createdConnector.actionTypeId)) {
6261
connectors.reloadConnectors();
6362
}
64-
65-
if (
66-
!knowledgeBase.status.value ||
67-
knowledgeBase.status.value?.kbState === KnowledgeBaseState.NOT_INSTALLED
68-
) {
69-
knowledgeBase.install();
70-
}
7163
};
7264

7365
const ConnectorFlyout = useMemo(

x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/welcome_message_connectors.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
2222
import { isHttpFetchError } from '@kbn/core-http-browser';
2323
import type { UseGenAIConnectorsResult } from '../hooks/use_genai_connectors';
2424

25-
const fadeInAnimation = keyframes`
25+
export const fadeInAnimation = keyframes`
2626
from {
2727
opacity: 0;
2828
}

x-pack/platform/packages/shared/kbn-ai-assistant/src/chat/welcome_message_knowledge_base.tsx

Lines changed: 0 additions & 184 deletions
This file was deleted.

x-pack/platform/packages/shared/kbn-ai-assistant/src/hooks/__storybook_mocks__/use_knowledge_base.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ export function useKnowledgeBase(): UseKnowledgeBaseResult {
2020
value: {
2121
kbState: KnowledgeBaseState.NOT_INSTALLED,
2222
enabled: true,
23+
concreteWriteIndex: undefined,
24+
currentInferenceId: undefined,
25+
isReIndexing: false,
2326
},
2427
},
28+
warmupModel: async () => {},
29+
isWarmingUpModel: false,
2530
};
2631
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { renderHook, act } from '@testing-library/react';
9+
import { useInferenceEndpoints } from './use_inference_endpoints';
10+
import { useAIAssistantAppService } from './use_ai_assistant_app_service';
11+
12+
jest.mock('./use_ai_assistant_app_service');
13+
14+
describe('useInferenceEndpoints', () => {
15+
const mockCallApi = jest.fn();
16+
17+
beforeEach(() => {
18+
jest.clearAllMocks();
19+
20+
(useAIAssistantAppService as jest.Mock).mockReturnValue({
21+
callApi: mockCallApi,
22+
});
23+
});
24+
25+
it('fetches inference endpoints successfully on mount', async () => {
26+
const mockResponse = {
27+
endpoints: [
28+
{ id: '1', name: 'Endpoint 1' },
29+
{ id: '2', name: 'Endpoint 2' },
30+
],
31+
};
32+
33+
mockCallApi.mockResolvedValueOnce(mockResponse);
34+
35+
const { result } = renderHook(() => useInferenceEndpoints());
36+
37+
await act(async () => {
38+
await Promise.resolve();
39+
});
40+
41+
expect(mockCallApi).toHaveBeenCalledWith(
42+
'GET /internal/observability_ai_assistant/kb/inference_endpoints',
43+
{
44+
signal: expect.any(AbortSignal),
45+
}
46+
);
47+
48+
expect(result.current.inferenceEndpoints).toEqual(mockResponse.endpoints);
49+
expect(result.current.isLoading).toBe(false);
50+
expect(result.current.error).toBeUndefined();
51+
});
52+
53+
it('sets an error state on API errors', async () => {
54+
const error = new Error('Something went wrong');
55+
mockCallApi.mockRejectedValueOnce(error);
56+
57+
const { result } = renderHook(() => useInferenceEndpoints());
58+
59+
await act(async () => {
60+
await Promise.resolve();
61+
});
62+
63+
expect(result.current.inferenceEndpoints).toEqual([]);
64+
expect(result.current.isLoading).toBe(false);
65+
expect(result.current.error).toEqual(error);
66+
});
67+
68+
it('ignores AbortError and does not set error state', async () => {
69+
const abortError = new DOMException('Aborted', 'AbortError');
70+
mockCallApi.mockRejectedValueOnce(abortError);
71+
72+
const { result } = renderHook(() => useInferenceEndpoints());
73+
74+
await act(async () => {
75+
await Promise.resolve();
76+
});
77+
78+
expect(result.current.inferenceEndpoints).toEqual([]);
79+
expect(result.current.isLoading).toBe(false);
80+
expect(result.current.error).toBeUndefined();
81+
});
82+
});

0 commit comments

Comments
 (0)