Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ When updating Security AI Prompts saved objects in the `elastic/integrations` re

```bash
cd $INTEGRATIONS_HOME/packages/security_ai_prompts/kibana/security_ai_prompt
rm ./*.json
cp $KIBANA_HOME/target/security_ai_prompts/*.json .
```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { Prompt } from '@kbn/security-ai-prompts';
import {
SAVED_OBJECT_ID_PREFIX,
generateSavedObject,
generateSavedObjects,
generateStableId,
} from './generate_security_ai_prompts_script';

const basePrompt: Prompt = {
promptGroupId: 'aiAssistant',
promptId: 'systemPrompt',
prompt: { default: 'You are a helpful assistant.' },
};

describe('generateStableId', () => {
it('converts camelCase to kebab-case', () => {
const id = generateStableId(basePrompt);
expect(id).toBe(`${SAVED_OBJECT_ID_PREFIX}ai-assistant-system-prompt`);
});

it('handles acronyms correctly (e.g. ESQL stays grouped)', () => {
const id = generateStableId({ ...basePrompt, promptId: 'NaturalLanguageESQLTool' });
expect(id).toBe(`${SAVED_OBJECT_ID_PREFIX}ai-assistant-natural-language-esql-tool`);
});

it('appends provider when present', () => {
const id = generateStableId({ ...basePrompt, provider: 'openai' });
expect(id).toBe(`${SAVED_OBJECT_ID_PREFIX}ai-assistant-system-prompt-openai`);
});

it('appends model when present', () => {
const id = generateStableId({ ...basePrompt, provider: 'openai', model: 'gpt-4o' });
expect(id).toBe(`${SAVED_OBJECT_ID_PREFIX}ai-assistant-system-prompt-openai-gpt-4o`);
});

it('omits model segment when provider is absent', () => {
const id = generateStableId({ ...basePrompt, model: 'gpt-4o' });
expect(id).toBe(`${SAVED_OBJECT_ID_PREFIX}ai-assistant-system-prompt`);
});

it('does not collide when provider-only vs model-only have the same value', () => {
const providerOnly = generateStableId({ ...basePrompt, provider: 'openai' });
const modelOnly = generateStableId({ ...basePrompt, model: 'openai' });
expect(providerOnly).not.toBe(modelOnly);
});

it('is fully lowercase', () => {
const id = generateStableId({
...basePrompt,
promptGroupId: 'AttackDiscovery',
promptId: 'SystemPrompt',
provider: 'OpenAI',
});
expect(id).toBe(id.toLowerCase());
});

it('returns the same id on repeated calls (stable)', () => {
expect(generateStableId(basePrompt)).toBe(generateStableId(basePrompt));
});

it('produces distinct ids for different providers', () => {
const openai = generateStableId({ ...basePrompt, provider: 'openai' });
const bedrock = generateStableId({ ...basePrompt, provider: 'bedrock' });
expect(openai).not.toBe(bedrock);
});

it('produces distinct ids for different promptGroupIds', () => {
const a = generateStableId({ ...basePrompt, promptGroupId: 'aiAssistant' });
const b = generateStableId({ ...basePrompt, promptGroupId: 'attackDiscovery' });
expect(a).not.toBe(b);
});

it('produces distinct ids for different promptIds', () => {
const a = generateStableId({ ...basePrompt, promptId: 'systemPrompt' });
const b = generateStableId({ ...basePrompt, promptId: 'userPrompt' });
expect(a).not.toBe(b);
});
});

describe('generateSavedObject', () => {
it('sets a stable id', () => {
const result = generateSavedObject(basePrompt);
expect(result.id).toBe(generateStableId(basePrompt));
});

it('sets type to security-ai-prompt', () => {
expect(generateSavedObject(basePrompt).type).toBe('security-ai-prompt');
});

it('preserves prompt attributes', () => {
const prompt: Prompt = { ...basePrompt, provider: 'bedrock', description: 'My prompt' };
const { attributes } = generateSavedObject(prompt);
expect(attributes.promptId).toBe(prompt.promptId);
expect(attributes.promptGroupId).toBe(prompt.promptGroupId);
expect(attributes.provider).toBe('bedrock');
expect(attributes.description).toBe('My prompt');
expect(attributes.prompt.default).toBe(prompt.prompt.default);
});
});

describe('generateSavedObjects', () => {
it('maps each prompt to a saved object with a unique stable id', () => {
const prompts: Prompt[] = [
{ ...basePrompt, promptId: 'systemPrompt' },
{ ...basePrompt, promptId: 'userPrompt' },
{ ...basePrompt, promptId: 'systemPrompt', provider: 'openai' },
];
const results = generateSavedObjects(prompts);
const ids = results.map((r) => r.id);
expect(new Set(ids).size).toBe(ids.length);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import * as fs from 'fs/promises';
import { existsSync, mkdirSync } from 'fs';
import * as path from 'path';
import globby from 'globby';
import { v4 as uuidv4 } from 'uuid';

import { localPrompts } from '../server/lib/prompt/local_prompt_object';
import { localToolPrompts } from '../server/lib/prompt/tool_prompts';
Expand Down Expand Up @@ -76,14 +75,29 @@ export const writeSavedObjects = async ({
}
};

const toKebabCase = (str: string): string =>
str
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
.toLowerCase();

export const generateStableId = ({ promptGroupId, promptId, provider, model }: Prompt): string => {
const parts = [SAVED_OBJECT_ID_PREFIX + toKebabCase(promptGroupId), toKebabCase(promptId)];
if (provider) {
parts.push(toKebabCase(provider));
if (model) parts.push(toKebabCase(model));
}
return parts.join('-');
};
Comment thread
macroscopeapp[bot] marked this conversation as resolved.

export const generateSavedObject = (prompt: Prompt): SecurityAiPromptSavedObject => ({
attributes: {
...prompt,
prompt: {
default: `${prompt.prompt.default}`,
},
},
id: `${SAVED_OBJECT_ID_PREFIX}${uuidv4()}`,
id: generateStableId(prompt),
type: 'security-ai-prompt',
});

Expand Down
Loading