Skip to content

Commit

Permalink
Merge pull request elastic#3 from yansavitski/ai-playground-chat
Browse files Browse the repository at this point in the history
Ai playground chat
  • Loading branch information
yansavitski authored Feb 6, 2024
2 parents f172e64 + 4ae846c commit ac2b65e
Show file tree
Hide file tree
Showing 11 changed files with 447 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@
import React from 'react';
import { i18n } from '@kbn/i18n';

import { EuiFlexGroup, EuiFlexItem, EuiPageTemplate, useEuiTheme } from '@elastic/eui';
import { EuiPageTemplate } from '@elastic/eui';

import { EnterpriseSearchContentPageTemplate } from '../../layout/page_template';
import { AIPlaygroundSidebar } from './ai_playground_sidebar';
import { Chat } from './chat';

export const AIPlayground: React.FC = () => {
const { euiTheme } = useEuiTheme();

return (
<EnterpriseSearchContentPageTemplate
pageChrome={[
Expand All @@ -43,26 +41,7 @@ export const AIPlayground: React.FC = () => {
contentProps={{ css: { display: 'flex', flexGrow: 1 } }}
paddingSize="none"
>
<EuiFlexGroup gutterSize="none">
<EuiFlexItem
grow={2}
css={{
borderRight: euiTheme.border.thin,
padding: euiTheme.size.l,
}}
>
<EuiFlexGroup direction="column" justifyContent="spaceBetween"></EuiFlexGroup>
</EuiFlexItem>

<EuiFlexItem
grow={1}
css={{
padding: euiTheme.size.l,
}}
>
<AIPlaygroundSidebar />
</EuiFlexItem>
</EuiFlexGroup>
<Chat />
</EuiPageTemplate.Section>
</EnterpriseSearchContentPageTemplate>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
*
* * 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 {
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiLink,
useEuiTheme,
} from '@elastic/eui';
import React from 'react';
import { QuestionInput } from './question_input';
import { i18n } from '@kbn/i18n';
import { ChatSidebar } from './chat_sidebar';
import { TelegramIcon } from './telegram_icon';
import { MessageList } from './message_list/message_list';
import { MessageRole } from '../types';

export const Chat = () => {
const { euiTheme } = useEuiTheme();
const [question, setQuestion] = React.useState('');
const isSendButtonDisabled = !question.trim();
const messages = [
{
id: 'sfs',
role: MessageRole.system,
content: (
<>
Added <EuiLink>5 indices</EuiLink> to chat context.
</>
),
},
{
id: 'asaqwas',
role: MessageRole.assistant,
createdAt: new Date(),
content:
'We went ahead and added your indices. You can start chatting now or modify your sources. For a deeper dive into AI Playground, review our documentation.',
},
{
id: 'asdas',
role: MessageRole.user,
createdAt: new Date(),
content: 'What is the average response time?',
},
];

return (
<EuiFlexGroup gutterSize="none">
<EuiFlexItem
grow={2}
css={{
borderRight: euiTheme.border.thin,
padding: euiTheme.size.l,
}}
>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={1}>
<MessageList messages={messages} />
</EuiFlexItem>

<EuiHorizontalRule margin="none" />

<EuiFlexItem grow={false}>
<QuestionInput
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.questionInput.askQuestionPlaceholder',
{
defaultMessage: 'Ask a question',
}
)}
value={question}
onChange={setQuestion}
button={
<EuiButtonIcon
aria-label={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.sendButtonAriaLabel',
{
defaultMessage: 'Send a question',
}
)}
display={isSendButtonDisabled ? 'empty' : 'base'}
size="s"
type="submit"
isDisabled={isSendButtonDisabled}
iconType={TelegramIcon}
/>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>

<EuiFlexItem
grow={1}
css={{
padding: euiTheme.size.l,
}}
>
<ChatSidebar />
</EuiFlexItem>
</EuiFlexGroup>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { EuiFormRow, EuiSwitch } from '@elastic/eui';

import { InstructionsField } from './instructions_field';

export const AIPlaygroundSidebar: React.FC = () => {
export const ChatSidebar: React.FC = () => {
const [prompt, setPrompt] = React.useState('');
const [isIncludeCitations, setIncludeCitations] = React.useState<boolean>(true);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
*
* * 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 React from 'react';
import moment from 'moment';
import { EuiComment, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { CopyActionButton } from './copy_action_button';
import type { Message as MessageType } from '../../types';

interface AssistantMessageProps extends 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', {
defaultMessage: 'responded',
})}
timestamp={
createdAt &&
i18n.translate('xpack.enterpriseSearch.content.aiPlayground.message.assistant.createdAt', {
defaultMessage: 'on {date}',
values: {
date: moment(createdAt).format('MMM DD, YYYY'),
},
})
}
timelineAvatar="compute"
timelineAvatarAriaLabel={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.assistant.avatarAriaLabel',
{
defaultMessage: 'AI',
}
)}
actions={
<CopyActionButton
copyText={String(content)}
ariaLabel={i18n.translate(
'xpack.enterpriseSearch.content.aiPlayground.message.assistant.copyLabel',
{ defaultMessage: 'Copy assistant message' }
)}
/>
}
>
<EuiText size="s">
<p>{content}</p>
</EuiText>
</EuiComment>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
*
* * 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 React from 'react';
import { EuiButtonIcon } from '@elastic/eui';

interface CopyActionButtonProps {
copyText: string;
ariaLabel: string;
}

export const CopyActionButton: React.FC<CopyActionButtonProps> = ({ copyText, ariaLabel }) => {
const handleCopy = () => {
navigator.clipboard.writeText(copyText);
};

return (
<EuiButtonIcon
aria-label={ariaLabel}
color="text"
iconType="copyClipboard"
onClick={handleCopy}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
*
* * 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 React from 'react';
import { EuiCommentList } from '@elastic/eui';
import { Message, MessageRole } from '../../types';
import { SystemMessage } from './system_message';
import { UserMessage } from './user_message';
import { AssistantMessage } from './assistant_message';

interface MessageListProps {
messages: Message[];
}

export const MessageList: React.FC<MessageListProps> = ({ messages }) => {
const mapRoleToMessageComponent = {
[MessageRole.system]: (message: Message) => <SystemMessage content={message.content} />,
[MessageRole.user]: (message: Message) => (
<UserMessage content={message.content} createdAt={message.createdAt} />
),
[MessageRole.assistant]: (message: Message) => (
<AssistantMessage content={message.content} createdAt={message.createdAt} />
),
};

return (
<EuiCommentList gutterSize="m">
{messages.map((message) => (
<React.Fragment key={message.id}>
{mapRoleToMessageComponent[message.role](message)}
</React.Fragment>
))}
</EuiCommentList>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
*
* * 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 React from 'react';
import { EuiComment } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

interface SystemMessageProps {
content: React.ReactNode;
}

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',
}
)}
event={content}
timelineAvatar="dot"
eventColor="subdued"
/>
);
};
Loading

0 comments on commit ac2b65e

Please sign in to comment.