-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[8.x] [Security assistant] Conversation pagination patch MIN (#197305) (
#197558) # Backport This will backport the following commits from `main` to `8.x`: - [[Security assistant] Conversation pagination patch MIN (#197305)](#197305) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Steph Milovic","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-24T02:25:17Z","message":"[Security assistant] Conversation pagination patch MIN (#197305)","sha":"de876fbd1b7a216565eb24b75b8453ee16a4641a","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","Team: SecuritySolution","backport:prev-major","Team:Security Generative AI","v8.16.0","v8.17.0","v8.15.4"],"number":197305,"url":"https://github.com/elastic/kibana/pull/197305","mergeCommit":{"message":"[Security assistant] Conversation pagination patch MIN (#197305)","sha":"de876fbd1b7a216565eb24b75b8453ee16a4641a"}},"sourceBranch":"main","suggestedTargetBranches":["8.x","8.15"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197305","number":197305,"mergeCommit":{"message":"[Security assistant] Conversation pagination patch MIN (#197305)","sha":"de876fbd1b7a216565eb24b75b8453ee16a4641a"}},{"branch":"8.16","label":"v8.16.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/197557","number":197557,"state":"OPEN"},{"branch":"8.x","label":"v8.17.0","labelRegex":"^v8.17.0$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.15","label":"v8.15.4","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT-->
- Loading branch information
1 parent
3fe8a5d
commit 820e34e
Showing
11 changed files
with
278 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
x-pack/plugins/elastic_assistant/scripts/create_conversations.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
require('../../../../src/setup_node_env'); | ||
require('./create_conversations_script').create(); |
165 changes: 165 additions & 0 deletions
165
x-pack/plugins/elastic_assistant/scripts/create_conversations_script.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { randomBytes } from 'node:crypto'; | ||
import yargs from 'yargs/yargs'; | ||
import { ToolingLog } from '@kbn/tooling-log'; | ||
import axios from 'axios'; | ||
import { | ||
API_VERSIONS, | ||
ConversationCreateProps, | ||
ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL, | ||
} from '@kbn/elastic-assistant-common'; | ||
import pLimit from 'p-limit'; | ||
import { getCreateConversationSchemaMock } from '../server/__mocks__/conversations_schema.mock'; | ||
|
||
/** | ||
* Developer script for creating conversations. | ||
* node x-pack/plugins/elastic_assistant/scripts/create_conversations | ||
*/ | ||
export const create = async () => { | ||
const logger = new ToolingLog({ | ||
level: 'info', | ||
writeTo: process.stdout, | ||
}); | ||
const argv = yargs(process.argv.slice(2)) | ||
.option('count', { | ||
type: 'number', | ||
description: 'Number of conversations to create', | ||
default: 100, | ||
}) | ||
.option('kibana', { | ||
type: 'string', | ||
description: 'Kibana url including auth', | ||
default: `http://elastic:changeme@localhost:5601`, | ||
}) | ||
.parse(); | ||
const kibanaUrl = removeTrailingSlash(argv.kibana); | ||
const count = Number(argv.count); | ||
logger.info(`Kibana URL: ${kibanaUrl}`); | ||
const connectorsApiUrl = `${kibanaUrl}/api/actions/connectors`; | ||
const conversationsCreateUrl = `${kibanaUrl}${ELASTIC_AI_ASSISTANT_CONVERSATIONS_URL}`; | ||
|
||
try { | ||
logger.info(`Fetching available connectors...`); | ||
const { data: connectors } = await axios.get(connectorsApiUrl, { | ||
headers: requestHeaders, | ||
}); | ||
const aiConnectors = connectors.filter( | ||
({ connector_type_id: connectorTypeId }: { connector_type_id: string }) => | ||
AllowedActionTypeIds.includes(connectorTypeId) | ||
); | ||
if (aiConnectors.length === 0) { | ||
throw new Error('No AI connectors found, create an AI connector to use this script'); | ||
} | ||
|
||
logger.info(`Creating ${count} conversations...`); | ||
if (count > 999) { | ||
logger.info(`This may take a couple of minutes...`); | ||
} | ||
|
||
const promises = Array.from({ length: count }, (_, i) => | ||
limit(() => | ||
retryRequest( | ||
() => | ||
axios.post( | ||
conversationsCreateUrl, | ||
getCreateConversationSchemaMock({ | ||
...getMockConversationContent(), | ||
apiConfig: { | ||
actionTypeId: aiConnectors[0].connector_type_id, | ||
connectorId: aiConnectors[0].id, | ||
}, | ||
}), | ||
{ headers: requestHeaders } | ||
), | ||
3, // Retry up to 3 times | ||
1000 // Delay of 1 second between retries | ||
) | ||
) | ||
); | ||
|
||
const results = await Promise.allSettled(promises); | ||
|
||
const successfulResults = results.filter((result) => result.status === 'fulfilled'); | ||
const errorResults = results.filter( | ||
(result) => result.status === 'rejected' | ||
) as PromiseRejectedResult[]; | ||
const conversationsCreated = successfulResults.length; | ||
|
||
if (count > conversationsCreated) { | ||
const errorExample = | ||
errorResults.length > 0 ? errorResults[0]?.reason?.message ?? 'unknown' : 'unknown'; | ||
throw new Error( | ||
`Failed to create all conversations. Expected count: ${count}, Created count: ${conversationsCreated}. Reason: ${errorExample}` | ||
); | ||
} | ||
logger.info(`Successfully created ${successfulResults.length} conversations.`); | ||
} catch (e) { | ||
logger.error(e); | ||
} | ||
}; | ||
// Set the concurrency limit (e.g., 50 requests at a time) | ||
const limit = pLimit(50); | ||
|
||
// Retry helper function | ||
const retryRequest = async ( | ||
fn: () => Promise<unknown>, | ||
retries: number = 3, | ||
delay: number = 1000 | ||
): Promise<unknown> => { | ||
try { | ||
return await fn(); | ||
} catch (e) { | ||
if (retries > 0) { | ||
await new Promise((res) => setTimeout(res, delay)); | ||
return retryRequest(fn, retries - 1, delay); | ||
} | ||
throw e; // If retries are exhausted, throw the error | ||
} | ||
}; | ||
|
||
const getMockConversationContent = (): Partial<ConversationCreateProps> => ({ | ||
title: `A ${randomBytes(4).toString('hex')} title`, | ||
isDefault: false, | ||
messages: [ | ||
{ | ||
content: 'Hello robot', | ||
role: 'user', | ||
timestamp: '2019-12-13T16:40:33.400Z', | ||
traceData: { | ||
traceId: '1', | ||
transactionId: '2', | ||
}, | ||
}, | ||
{ | ||
content: 'Hello human', | ||
role: 'assistant', | ||
timestamp: '2019-12-13T16:41:33.400Z', | ||
traceData: { | ||
traceId: '3', | ||
transactionId: '4', | ||
}, | ||
}, | ||
], | ||
}); | ||
|
||
export const AllowedActionTypeIds = ['.bedrock', '.gen-ai', '.gemini']; | ||
|
||
const requestHeaders = { | ||
'kbn-xsrf': 'xxx', | ||
'Content-Type': 'application/json', | ||
'elastic-api-version': API_VERSIONS.public.v1, | ||
}; | ||
|
||
function removeTrailingSlash(url: string) { | ||
if (url.endsWith('/')) { | ||
return url.slice(0, -1); | ||
} else { | ||
return url; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.