Skip to content

Commit

Permalink
Merge pull request elastic#5 from yansavitski/ai-playground-plugin
Browse files Browse the repository at this point in the history
Ai playground plugin
  • Loading branch information
yansavitski authored Feb 7, 2024
2 parents 871407e + 1bc6e86 commit aad8adf
Show file tree
Hide file tree
Showing 24 changed files with 171 additions and 105 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
"@kbn/advanced-settings-plugin": "link:src/plugins/advanced_settings",
"@kbn/ai-assistant-management-observability-plugin": "link:src/plugins/ai_assistant_management/observability",
"@kbn/ai-assistant-management-plugin": "link:src/plugins/ai_assistant_management/selection",
"@kbn/ai-playground": "link:packages/kbn-ai-playground",
"@kbn/aiops-components": "link:x-pack/packages/ml/aiops_components",
"@kbn/aiops-plugin": "link:x-pack/plugins/aiops",
"@kbn/aiops-utils": "link:x-pack/packages/ml/aiops_utils",
Expand Down
3 changes: 3 additions & 0 deletions packages/kbn-ai-playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @kbn/ai-playground

Empty package generated by @kbn/generate
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const Chat = () => {
<EuiFlexItem grow={false}>
<QuestionInput
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.questionInput.askQuestionPlaceholder',
'aiPlayground.chat.questionInput.askQuestionPlaceholder',
{
defaultMessage: 'Ask a question',
}
Expand All @@ -83,12 +83,9 @@ export const Chat = () => {
onChange={setQuestion}
button={
<EuiButtonIcon
aria-label={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.sendButtonAriaLabel',
{
defaultMessage: 'Send a question',
}
)}
aria-label={i18n.translate('aiPlayground.chat.sendButtonAriaLabel', {
defaultMessage: 'Send a question',
})}
display={isSendButtonDisabled ? 'empty' : 'base'}
size="s"
type="submit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,25 @@ export const ChatSidebar: React.FC = () => {
return (
<>
<InstructionsField
label={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.instructionsField.label',
{
defaultMessage: 'Instructions',
}
)}
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.instructionsField.placeholder',
{
defaultMessage: 'Replace me',
}
)}
helpText={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.instructionsField.help',
{
defaultMessage:
'This is the instruction or question you want the AI to respond to. Be clear and specific for the best results.',
}
)}
label={i18n.translate('aiPlayground.sidebar.instructionsField.label', {
defaultMessage: 'Instructions',
})}
placeholder={i18n.translate('aiPlayground.sidebar.instructionsField.placeholder', {
defaultMessage: 'Replace me',
})}
helpText={i18n.translate('aiPlayground.sidebar.instructionsField.help', {
defaultMessage:
'This is the instruction or question you want the AI to respond to. Be clear and specific for the best results.',
})}
value={prompt}
onChange={setPrompt}
/>

<EuiFormRow>
<EuiSwitch
label={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.citationsField.label',
{
defaultMessage: 'Include citations',
}
)}
label={i18n.translate('aiPlayground.sidebar.citationsField.label', {
defaultMessage: 'Include citations',
})}
checked={isIncludeCitations}
onChange={(e) => setIncludeCitations(e.target.checked)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,29 @@
* 2.0.
*/

import { mockKibanaValues } from '../../../../__mocks__/kea_logic';

import React from 'react';

import { mount } from 'enzyme';

import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';

import { NEW_INDEX_PATH } from '../../../routes';

import { EmptyIndex } from './empty_index';

describe('Empty state', () => {
it('renders the empty state component', () => {
const wrapper = mount(<EmptyIndex />);
const wrapper = mount(<EmptyIndex onCreateIndexClick={() => {}} />);

expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1);
expect(wrapper.find(EuiButton)).toHaveLength(1);
});

it('clicking in navigates to new index page', () => {
const { navigateToUrl } = mockKibanaValues;
const onCreateIndexClick = jest.fn();
const wrapper = mount(<EmptyIndex onCreateIndexClick={onCreateIndexClick} />);

const wrapper = mount(<EmptyIndex />);
// @ts-ignore
wrapper.find(EuiButton).props().onClick();

expect(navigateToUrl).toHaveBeenCalledTimes(1);
expect(navigateToUrl).toHaveBeenCalledWith(NEW_INDEX_PATH);
expect(onCreateIndexClick).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,31 @@ import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiPanel } from '

import { i18n } from '@kbn/i18n';

import { KibanaLogic } from '../../../../shared/kibana';
import { NEW_INDEX_PATH } from '../../../routes';
interface EmptyIndexProps {
onCreateIndexClick: () => void;
}

export const EmptyIndex: React.FC = () => {
export const EmptyIndex: React.FC<EmptyIndexProps> = ({ onCreateIndexClick }) => {
return (
<EuiFlexGroup gutterSize="l">
<EuiFlexItem>
<EuiPanel>
<EuiEmptyPrompt
title={
<h2>
{i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.emptyIndex.h2.addData',
{
defaultMessage: 'Add data',
}
)}
{i18n.translate('aiPlayground.emptyIndex.h2.addData', {
defaultMessage: 'Add data',
})}
</h2>
}
iconType="plusInCircle"
titleSize="m"
color="subdued"
body={
<p>
{i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.emptyIndex.p.addDataAndIndexLabel',
{
defaultMessage: 'To use the AI Playground, create an index and add some data.',
}
)}
{i18n.translate('aiPlayground.emptyIndex.p.addDataAndIndexLabel', {
defaultMessage: 'To use the AI Playground, create an index and add some data.',
})}
</p>
}
actions={
Expand All @@ -48,14 +44,11 @@ export const EmptyIndex: React.FC = () => {
disabled={false}
fill
iconType="plusInCircle"
onClick={() => KibanaLogic.values.navigateToUrl(NEW_INDEX_PATH)}
onClick={onCreateIndexClick}
>
{i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.emptyIndex.newIndexButtonLabel',
{
defaultMessage: 'Create an index',
}
)}
{i18n.translate('aiPlayground.emptyIndex.newIndexButtonLabel', {
defaultMessage: 'Create an index',
})}
</EuiButton>
}
/>
Expand Down
11 changes: 11 additions & 0 deletions packages/kbn-ai-playground/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
*
* * 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.
*
*/

export * from './chat';
export * from './empty_index';
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,15 @@ type AssistantMessageProps = Pick<MessageType, 'content' | 'createdAt'>;
export const AssistantMessage: React.FC<AssistantMessageProps> = ({ content, createdAt }) => {
return (
<EuiComment
username={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.assistant.username',
{
defaultMessage: 'AI',
}
)}
event={i18n.translate('xpack.enterpriseSearch.content.aiPlayground.message.assistant.event', {
username={i18n.translate('aiPlayground.chat.message.assistant.username', {
defaultMessage: 'AI',
})}
event={i18n.translate('aiPlayground.chat.message.assistant.event', {
defaultMessage: 'responded',
})}
timestamp={
createdAt &&
i18n.translate('xpack.enterpriseSearch.content.aiPlayground.message.assistant.createdAt', {
i18n.translate('aiPlayground.chat.message.assistant.createdAt', {
defaultMessage: 'on {date}',
values: {
date: moment(createdAt).format('MMM DD, YYYY'),
Expand All @@ -41,18 +38,17 @@ export const AssistantMessage: React.FC<AssistantMessageProps> = ({ content, cre
}
timelineAvatar="compute"
timelineAvatarAriaLabel={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.assistant.avatarAriaLabel',
'aiPlayground.chat.message.assistant.avatarAriaLabel',
{
defaultMessage: 'AI',
}
)}
actions={
<CopyActionButton
copyText={String(content)}
ariaLabel={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.assistant.copyLabel',
{ defaultMessage: 'Copy assistant message' }
)}
ariaLabel={i18n.translate('aiPlayground.chat.message.assistant.copyLabel', {
defaultMessage: 'Copy assistant message',
})}
/>
}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,12 @@ interface SystemMessageProps {
export const SystemMessage: React.FC<SystemMessageProps> = ({ content }) => {
return (
<EuiComment
username={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.system.username',
{
defaultMessage: 'system',
}
)}
timelineAvatarAriaLabel={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.system.avatarAriaLabel',
{
defaultMessage: 'System',
}
)}
username={i18n.translate('aiPlayground.chat.message.system.username', {
defaultMessage: 'system',
})}
timelineAvatarAriaLabel={i18n.translate('aiPlayground.chat.message.system.avatarAriaLabel', {
defaultMessage: 'System',
})}
event={content}
timelineAvatar="dot"
eventColor="subdued"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,36 @@

import React, { useEffect, useState } from 'react';

import { useValues } from 'kea';
import moment from 'moment';

import { EuiComment, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { UserAvatar, UserProfileWithAvatar } from '@kbn/user-profile-components';

import { KibanaLogic } from '../../../../../shared/kibana';
import type { Message as MessageType } from '../../types';
import type { Message as MessageType, AIPlaygroundPluginStartDeps } from '../../types';

import { CopyActionButton } from './copy_action_button';

type UserMessageProps = Pick<MessageType, 'content' | 'createdAt'>;

export const UserMessage: React.FC<UserMessageProps> = ({ content, createdAt }) => {
const { security } = useValues(KibanaLogic);
const { services } = useKibana<AIPlaygroundPluginStartDeps>();
const [currentUserProfile, setCurrentUserProfile] = useState<UserProfileWithAvatar>();

useEffect(() => {
security.userProfiles.getCurrent({ dataPath: 'avatar' }).then(setCurrentUserProfile);
}, [security]);
services.security?.userProfiles.getCurrent({ dataPath: 'avatar' }).then(setCurrentUserProfile);
}, [services]);

return (
<EuiComment
username={currentUserProfile?.user.username}
event={i18n.translate('xpack.enterpriseSearch.content.aiPlayground.message.user.event', {
event={i18n.translate('aiPlayground.chat.message.user.event', {
defaultMessage: 'asked',
})}
timestamp={
createdAt &&
i18n.translate('xpack.enterpriseSearch.content.aiPlayground.message.user.createdAt', {
i18n.translate('aiPlayground.chat.message.user.createdAt', {
defaultMessage: 'on {date}',
values: {
date: moment(createdAt).format('MMM DD, YYYY'),
Expand All @@ -51,10 +50,9 @@ export const UserMessage: React.FC<UserMessageProps> = ({ content, createdAt })
actions={
<CopyActionButton
copyText={String(content)}
ariaLabel={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.user.copyLabel',
{ defaultMessage: 'Copy user message' }
)}
ariaLabel={i18n.translate('aiPlayground.chat.message.user.copyLabel', {
defaultMessage: 'Copy user message',
})}
/>
}
>
Expand Down
9 changes: 9 additions & 0 deletions packages/kbn-ai-playground/index.ts
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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export * from './components';
13 changes: 13 additions & 0 deletions packages/kbn-ai-playground/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-ai-playground'],
};
5 changes: 5 additions & 0 deletions packages/kbn-ai-playground/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/ai-playground",
"owner": "@elastic/enterprise-search-frontend"
}
6 changes: 6 additions & 0 deletions packages/kbn-ai-playground/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@kbn/ai-playground",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}
22 changes: 22 additions & 0 deletions packages/kbn-ai-playground/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/i18n",
"@kbn/core",
]
}
Loading

0 comments on commit aad8adf

Please sign in to comment.