@aws-amplify/ui-react-ai 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/AIConversation/AIConversation.mjs +2 -1
- package/dist/esm/components/AIConversation/context/DisplayTextContext.mjs +9 -0
- package/dist/esm/components/AIConversation/context/ResponseComponentsContext.mjs +10 -2
- package/dist/esm/components/AIConversation/createAIConversation.mjs +2 -1
- package/dist/esm/components/AIConversation/createProvider.mjs +19 -11
- package/dist/esm/components/AIConversation/displayText.mjs +7 -0
- package/dist/esm/components/AIConversation/views/Controls/ActionsBarControl.mjs +5 -0
- package/dist/esm/components/AIConversation/views/Controls/AttachFileControl.mjs +5 -0
- package/dist/esm/components/AIConversation/views/Controls/AttachmentListControl.mjs +5 -0
- package/dist/esm/components/AIConversation/views/Controls/AvatarControl.mjs +5 -0
- package/dist/esm/components/AIConversation/views/Controls/FieldControl.mjs +6 -5
- package/dist/esm/components/AIConversation/views/Controls/MessagesControl.mjs +15 -6
- package/dist/esm/components/AIConversation/views/Controls/PromptControl.mjs +5 -1
- package/dist/esm/components/AIConversation/views/default/Form.mjs +5 -2
- package/dist/esm/components/AIConversation/views/default/MessageList.mjs +25 -3
- package/dist/index.js +155 -104
- package/dist/types/components/AIConversation/AIConversation.d.ts +4 -4
- package/dist/types/components/AIConversation/context/DisplayTextContext.d.ts +5 -0
- package/dist/types/components/AIConversation/context/ResponseComponentsContext.d.ts +1 -0
- package/dist/types/components/AIConversation/context/index.d.ts +11 -7
- package/dist/types/components/AIConversation/createProvider.d.ts +2 -2
- package/dist/types/components/AIConversation/displayText.d.ts +2 -2
- package/dist/types/components/AIConversation/index.d.ts +2 -1
- package/dist/types/components/AIConversation/views/default/Form.d.ts +1 -1
- package/dist/types/index.d.ts +3 -3
- package/package.json +4 -4
|
@@ -14,7 +14,7 @@ import { PromptList } from './views/default/PromptList.mjs';
|
|
|
14
14
|
import { ComponentClassName } from '@aws-amplify/ui';
|
|
15
15
|
import createProvider from './createProvider.mjs';
|
|
16
16
|
|
|
17
|
-
function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, }) {
|
|
17
|
+
function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, displayText, }) {
|
|
18
18
|
const icons = useIcons('aiConversation');
|
|
19
19
|
const defaultAvatars = {
|
|
20
20
|
ai: {
|
|
@@ -42,6 +42,7 @@ function AIConversationBase({ actions, avatars, controls, handleSendMessage, mes
|
|
|
42
42
|
Form,
|
|
43
43
|
...controls,
|
|
44
44
|
},
|
|
45
|
+
displayText,
|
|
45
46
|
});
|
|
46
47
|
const providerProps = {
|
|
47
48
|
messages,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContextUtilities } from '@aws-amplify/ui-react-core';
|
|
2
|
+
import { defaultAIConversationDisplayTextEn } from '../displayText.mjs';
|
|
3
|
+
|
|
4
|
+
const { ConversationDisplayTextContext, ConversationDisplayTextProvider, useConversationDisplayText, } = createContextUtilities({
|
|
5
|
+
contextName: 'ConversationDisplayText',
|
|
6
|
+
defaultValue: defaultAIConversationDisplayTextEn,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export { ConversationDisplayTextContext, ConversationDisplayTextProvider, useConversationDisplayText };
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
|
|
3
|
+
const RESPONSE_COMPONENT_PREFIX = 'AMPLIFY_UI_';
|
|
3
4
|
const ResponseComponentsContext = React__default.createContext(undefined);
|
|
5
|
+
const prependResponseComponents = (responseComponents) => {
|
|
6
|
+
if (!responseComponents)
|
|
7
|
+
return responseComponents;
|
|
8
|
+
return Object.keys(responseComponents).reduce((prev, key) => ((prev[`${RESPONSE_COMPONENT_PREFIX}${key}`] = responseComponents[key]),
|
|
9
|
+
prev), {});
|
|
10
|
+
};
|
|
4
11
|
const ResponseComponentsProvider = ({ children, responseComponents, }) => {
|
|
5
|
-
|
|
12
|
+
const _responseComponents = React__default.useMemo(() => prependResponseComponents(responseComponents), [responseComponents]);
|
|
13
|
+
return (React__default.createElement(ResponseComponentsContext.Provider, { value: _responseComponents }, children));
|
|
6
14
|
};
|
|
7
15
|
const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
8
16
|
if (!responseComponents) {
|
|
@@ -32,4 +40,4 @@ const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
|
32
40
|
return { tools };
|
|
33
41
|
};
|
|
34
42
|
|
|
35
|
-
export { ResponseComponentsContext, ResponseComponentsProvider, convertResponseComponentsToToolConfiguration };
|
|
43
|
+
export { RESPONSE_COMPONENT_PREFIX, ResponseComponentsContext, ResponseComponentsProvider, convertResponseComponentsToToolConfiguration };
|
|
@@ -12,7 +12,7 @@ import createProvider from './createProvider.mjs';
|
|
|
12
12
|
* @experimental
|
|
13
13
|
*/
|
|
14
14
|
function createAIConversation(input = {}) {
|
|
15
|
-
const { elements, suggestedPrompts, actions, responseComponents, variant, controls, } = input;
|
|
15
|
+
const { elements, suggestedPrompts, actions, responseComponents, variant, controls, displayText, } = input;
|
|
16
16
|
const Provider = createProvider({
|
|
17
17
|
elements,
|
|
18
18
|
actions,
|
|
@@ -20,6 +20,7 @@ function createAIConversation(input = {}) {
|
|
|
20
20
|
responseComponents,
|
|
21
21
|
variant,
|
|
22
22
|
controls,
|
|
23
|
+
displayText,
|
|
23
24
|
});
|
|
24
25
|
function AIConversation(props) {
|
|
25
26
|
const { messages, avatars, handleSendMessage, isLoading } = props;
|
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { ElementsProvider } from '@aws-amplify/ui-react-core/elements';
|
|
3
|
+
import { defaultAIConversationDisplayTextEn } from './displayText.mjs';
|
|
3
4
|
import { ActionsProvider } from './context/ActionsContext.mjs';
|
|
4
5
|
import { AvatarsProvider } from './context/AvatarsContext.mjs';
|
|
5
6
|
import { ConversationInputContextProvider } from './context/ConversationInputContext.mjs';
|
|
6
7
|
import { MessagesProvider } from './context/MessagesContext.mjs';
|
|
7
|
-
import { MessageVariantProvider } from './context/MessageVariantContext.mjs';
|
|
8
8
|
import { SuggestedPromptProvider } from './context/SuggestedPromptsContext.mjs';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { MessageVariantProvider } from './context/MessageVariantContext.mjs';
|
|
10
|
+
import { ConversationDisplayTextProvider } from './context/DisplayTextContext.mjs';
|
|
11
11
|
import { ControlsProvider } from './context/ControlsContext.mjs';
|
|
12
12
|
import { LoadingContextProvider } from './context/LoadingContext.mjs';
|
|
13
|
+
import { ResponseComponentsProvider } from './context/ResponseComponentsContext.mjs';
|
|
14
|
+
import { SendMessageContextProvider } from './context/SendMessageContext.mjs';
|
|
15
|
+
import './context/elements/definitions.mjs';
|
|
13
16
|
|
|
14
|
-
function createProvider({ elements, actions, suggestedPrompts, responseComponents, variant, controls, }) {
|
|
17
|
+
function createProvider({ elements, actions, suggestedPrompts, responseComponents, variant, controls, displayText, }) {
|
|
15
18
|
return function Provider({ children, messages, avatars, handleSendMessage, isLoading, }) {
|
|
19
|
+
const _displayText = {
|
|
20
|
+
...defaultAIConversationDisplayTextEn,
|
|
21
|
+
...displayText,
|
|
22
|
+
};
|
|
16
23
|
return (React__default.createElement(ElementsProvider, { elements: elements },
|
|
17
24
|
React__default.createElement(ControlsProvider, { controls: controls },
|
|
18
25
|
React__default.createElement(SuggestedPromptProvider, { suggestedPrompts: suggestedPrompts },
|
|
19
26
|
React__default.createElement(ResponseComponentsProvider, { responseComponents: responseComponents },
|
|
20
|
-
React__default.createElement(
|
|
21
|
-
React__default.createElement(
|
|
22
|
-
React__default.createElement(
|
|
23
|
-
React__default.createElement(
|
|
24
|
-
React__default.createElement(
|
|
25
|
-
React__default.createElement(
|
|
26
|
-
React__default.createElement(
|
|
27
|
+
React__default.createElement(ConversationDisplayTextProvider, { ..._displayText },
|
|
28
|
+
React__default.createElement(ConversationInputContextProvider, null,
|
|
29
|
+
React__default.createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
|
|
30
|
+
React__default.createElement(AvatarsProvider, { avatars: avatars },
|
|
31
|
+
React__default.createElement(ActionsProvider, { actions: actions },
|
|
32
|
+
React__default.createElement(MessageVariantProvider, { variant: variant },
|
|
33
|
+
React__default.createElement(MessagesProvider, { messages: messages },
|
|
34
|
+
React__default.createElement(LoadingContextProvider, { isLoading: isLoading }, children)))))))))))));
|
|
27
35
|
};
|
|
28
36
|
}
|
|
29
37
|
|
|
@@ -6,6 +6,11 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
6
6
|
import '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
|
|
11
16
|
const { Button, Span, View } = AIConversationElements;
|
|
@@ -6,6 +6,11 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
|
|
11
16
|
const { Button, Icon, View } = AIConversationElements;
|
|
@@ -6,6 +6,11 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
|
|
11
16
|
const { Button, Icon, ListItem, UnorderedList: ListElement, Span, Text, View, } = AIConversationElements;
|
|
@@ -6,6 +6,11 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
6
6
|
import { RoleContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
|
|
11
16
|
const { Icon, Span, Text, View } = AIConversationElements;
|
|
@@ -6,14 +6,15 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import { MessagesContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
11
|
+
import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
12
|
+
import { ResponseComponentsContext, convertResponseComponentsToToolConfiguration } from '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import { SendMessageContext } from '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
import { AttachFileControl } from './AttachFileControl.mjs';
|
|
11
16
|
import { AttachmentListControl } from './AttachmentListControl.mjs';
|
|
12
|
-
import { SendMessageContext } from '../../context/SendMessageContext.mjs';
|
|
13
|
-
import { ResponseComponentsContext, convertResponseComponentsToToolConfiguration } from '../../context/ResponseComponentsContext.mjs';
|
|
14
|
-
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
15
17
|
import { getImageTypeFromMimeType } from '../../utils.mjs';
|
|
16
|
-
import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
17
18
|
|
|
18
19
|
const { Button, Icon, Label: LabelElement, TextArea, View, } = AIConversationElements;
|
|
19
20
|
const FIELD_BLOCK = 'ai-field';
|
|
@@ -116,7 +117,7 @@ const FieldControl = () => {
|
|
|
116
117
|
const fileContent = {
|
|
117
118
|
image: {
|
|
118
119
|
format: getImageTypeFromMimeType(file.type),
|
|
119
|
-
source: { bytes: Uint8Array
|
|
120
|
+
source: { bytes: new Uint8Array(buffer) },
|
|
120
121
|
},
|
|
121
122
|
};
|
|
122
123
|
submittedContent.push(fileContent);
|
|
@@ -6,12 +6,15 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
6
6
|
import { RoleContext, MessagesContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import { MessageVariantContext } from '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import { useConversationDisplayText } from '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import { ResponseComponentsContext, RESPONSE_COMPONENT_PREFIX } from '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
|
-
import { convertBufferToBase64
|
|
15
|
+
import { convertBufferToBase64 } from '../../utils.mjs';
|
|
11
16
|
import { ActionsBarControl } from './ActionsBarControl.mjs';
|
|
12
17
|
import { AvatarControl } from './AvatarControl.mjs';
|
|
13
|
-
import { ResponseComponentsContext } from '../../context/ResponseComponentsContext.mjs';
|
|
14
|
-
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
15
18
|
|
|
16
19
|
const { Image, Span, Text, View } = AIConversationElements;
|
|
17
20
|
const MESSAGES_BLOCK = 'ai-messages';
|
|
@@ -43,7 +46,9 @@ const MessageControl = ({ message }) => {
|
|
|
43
46
|
else if (content.toolUse) {
|
|
44
47
|
// For now tool use is limited to custom response components
|
|
45
48
|
const { name, input } = content.toolUse;
|
|
46
|
-
if (!responseComponents ||
|
|
49
|
+
if (!responseComponents ||
|
|
50
|
+
!name ||
|
|
51
|
+
!name.startsWith(RESPONSE_COMPONENT_PREFIX)) {
|
|
47
52
|
return;
|
|
48
53
|
}
|
|
49
54
|
else {
|
|
@@ -81,6 +86,7 @@ const Layout = React__default.forwardRef(function Layout(props, ref) {
|
|
|
81
86
|
const MessagesControl = ({ renderMessage }) => {
|
|
82
87
|
const messages = React__default.useContext(MessagesContext);
|
|
83
88
|
const controls = React__default.useContext(ControlsContext);
|
|
89
|
+
const { getMessageTimestampText } = useConversationDisplayText();
|
|
84
90
|
const messagesRef = React__default.useRef([]);
|
|
85
91
|
const [focusedItemIndex, setFocusedItemIndex] = React__default.useState(messages ? messages.length - 1 : 0);
|
|
86
92
|
const handleFocus = (index) => setFocusedItemIndex(index);
|
|
@@ -113,13 +119,16 @@ const MessagesControl = ({ renderMessage }) => {
|
|
|
113
119
|
if (controls?.MessageList) {
|
|
114
120
|
return React__default.createElement(controls.MessageList, { messages: messages });
|
|
115
121
|
}
|
|
116
|
-
|
|
122
|
+
const messagesWithRenderableContent = messages?.filter((message) => message.content.some((content) => content.image ??
|
|
123
|
+
content.text ??
|
|
124
|
+
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
125
|
+
return (React__default.createElement(Layout, null, messagesWithRenderableContent?.map((message, index) => {
|
|
117
126
|
return renderMessage ? (renderMessage(message)) : (React__default.createElement(RoleContext.Provider, { value: message.role, key: `message-${index}` },
|
|
118
127
|
React__default.createElement(MessageContainer, { "data-testid": `message`, key: `message-${index}`, tabIndex: focusedItemIndex === index ? 0 : -1, onFocus: () => handleFocus(index), onKeyDown: (event) => onKeyDown(index, event), ref: (el) => (messagesRef.current[index] = el) },
|
|
119
128
|
React__default.createElement(HeaderContainer, null,
|
|
120
129
|
React__default.createElement(AvatarControl, null),
|
|
121
130
|
React__default.createElement(Separator, null),
|
|
122
|
-
React__default.createElement(Timestamp, null,
|
|
131
|
+
React__default.createElement(Timestamp, null, getMessageTimestampText(new Date(message.createdAt)))),
|
|
123
132
|
React__default.createElement(MessageControl, { message: message }),
|
|
124
133
|
message.role === 'assistant' ? (React__default.createElement(ActionsBarControl, { message: message, focusable: focusedItemIndex === index })) : null)));
|
|
125
134
|
})));
|
|
@@ -6,9 +6,13 @@ import { ConversationInputContext } from '../../context/ConversationInputContext
|
|
|
6
6
|
import { MessagesContext } from '../../context/MessagesContext.mjs';
|
|
7
7
|
import { SuggestedPromptsContext } from '../../context/SuggestedPromptsContext.mjs';
|
|
8
8
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
+
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
11
|
+
import '../../context/LoadingContext.mjs';
|
|
12
|
+
import '../../context/ResponseComponentsContext.mjs';
|
|
13
|
+
import '../../context/SendMessageContext.mjs';
|
|
9
14
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
10
15
|
import { classNames } from '@aws-amplify/ui';
|
|
11
|
-
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
12
16
|
|
|
13
17
|
const { View, Button, Text, Heading, Icon } = AIConversationElements;
|
|
14
18
|
const PROMPT_BLOCK = 'ai-prompts';
|
|
@@ -24,6 +24,9 @@ const Form = ({ setInput, input, handleSubmit, }) => {
|
|
|
24
24
|
React.createElement(View, { as: "form", className: ComponentClassName.AIConversationForm, onSubmit: handleSubmit },
|
|
25
25
|
React.createElement(Button, { className: ComponentClassName.AIConversationFormAttach, onClick: () => {
|
|
26
26
|
hiddenInput?.current?.click();
|
|
27
|
+
if (hiddenInput?.current) {
|
|
28
|
+
hiddenInput.current.value = '';
|
|
29
|
+
}
|
|
27
30
|
} },
|
|
28
31
|
React.createElement("span", null, attachIcon),
|
|
29
32
|
React.createElement(VisuallyHidden, null,
|
|
@@ -36,8 +39,8 @@ const Form = ({ setInput, input, handleSubmit, }) => {
|
|
|
36
39
|
...prevValue,
|
|
37
40
|
files: [...(prevValue?.files ?? []), ...Array.from(files)],
|
|
38
41
|
}));
|
|
39
|
-
}, multiple: true, accept: "*" }))),
|
|
40
|
-
React.createElement(TextAreaField, { className: ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', onKeyDown: (e) => {
|
|
42
|
+
}, multiple: true, accept: "*", "data-testid": "hidden-file-input" }))),
|
|
43
|
+
React.createElement(TextAreaField, { className: ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', testId: "text-input", onKeyDown: (e) => {
|
|
41
44
|
// Submit on enter key if shift is not pressed also
|
|
42
45
|
const shouldSubmit = !e.shiftKey && e.key === 'Enter';
|
|
43
46
|
if (shouldSubmit && isHTMLFormElement(e.target)) {
|
|
@@ -7,19 +7,35 @@ import '../../context/ConversationInputContext.mjs';
|
|
|
7
7
|
import { RoleContext } from '../../context/MessagesContext.mjs';
|
|
8
8
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
9
9
|
import { MessageVariantContext } from '../../context/MessageVariantContext.mjs';
|
|
10
|
+
import { useConversationDisplayText } from '../../context/DisplayTextContext.mjs';
|
|
11
|
+
import '../../context/ControlsContext.mjs';
|
|
12
|
+
import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
13
|
+
import { RESPONSE_COMPONENT_PREFIX } from '../../context/ResponseComponentsContext.mjs';
|
|
14
|
+
import '../../context/SendMessageContext.mjs';
|
|
10
15
|
import '../../context/elements/definitions.mjs';
|
|
11
|
-
import { formatDate } from '../../utils.mjs';
|
|
12
16
|
import { ComponentClassName, classNames, classNameModifier } from '@aws-amplify/ui';
|
|
13
17
|
|
|
14
18
|
const MessageMeta = ({ message }) => {
|
|
15
19
|
// need to pass this in as props in order for it to be overridable
|
|
16
20
|
const avatars = React.useContext(AvatarsContext);
|
|
17
21
|
const role = React.useContext(RoleContext);
|
|
22
|
+
const { getMessageTimestampText } = useConversationDisplayText();
|
|
18
23
|
// maybe rename 'avatar' to something else
|
|
19
24
|
const avatar = role === 'assistant' ? avatars?.ai : avatars?.user;
|
|
20
25
|
return (React.createElement(View, { className: ComponentClassName.AIConversationMessageSender },
|
|
21
26
|
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username),
|
|
22
|
-
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderTimestamp },
|
|
27
|
+
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderTimestamp }, getMessageTimestampText(new Date(message.createdAt)))));
|
|
28
|
+
};
|
|
29
|
+
const LoadingMessage = () => {
|
|
30
|
+
const avatars = React.useContext(AvatarsContext);
|
|
31
|
+
const variant = React.useContext(MessageVariantContext);
|
|
32
|
+
const avatar = avatars?.ai;
|
|
33
|
+
return (React.createElement(View, { className: classNames(ComponentClassName.AIConversationMessage, classNameModifier(ComponentClassName.AIConversationMessage, variant), classNameModifier(ComponentClassName.AIConversationMessage, 'assistant')) },
|
|
34
|
+
React.createElement(View, { className: ComponentClassName.AIConversationMessageAvatar },
|
|
35
|
+
React.createElement(Avatar, { isLoading: true }, avatar?.avatar)),
|
|
36
|
+
React.createElement(View, { className: ComponentClassName.AIConversationMessageBody },
|
|
37
|
+
React.createElement(View, { className: ComponentClassName.AIConversationMessageSender },
|
|
38
|
+
React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username)))));
|
|
23
39
|
};
|
|
24
40
|
const Message = ({ message }) => {
|
|
25
41
|
const avatars = React.useContext(AvatarsContext);
|
|
@@ -35,7 +51,13 @@ const Message = ({ message }) => {
|
|
|
35
51
|
React.createElement(MessageControl, { message: message }))))));
|
|
36
52
|
};
|
|
37
53
|
const MessageList = ({ messages, }) => {
|
|
38
|
-
|
|
54
|
+
const isLoading = React.useContext(LoadingContext);
|
|
55
|
+
const messagesWithRenderableContent = messages?.filter((message) => message.content.some((content) => content.image ??
|
|
56
|
+
content.text ??
|
|
57
|
+
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
58
|
+
return (React.createElement(View, { className: ComponentClassName.AIConversationMessageList },
|
|
59
|
+
messagesWithRenderableContent.map((message, i) => (React.createElement(Message, { key: `message-${i}`, message: message }))),
|
|
60
|
+
isLoading ? React.createElement(LoadingMessage, null) : null));
|
|
39
61
|
};
|
|
40
62
|
|
|
41
63
|
export { MessageList };
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var elements = require('@aws-amplify/ui-react-core/elements');
|
|
7
|
+
var uiReactCore = require('@aws-amplify/ui-react-core');
|
|
7
8
|
var ui = require('@aws-amplify/ui');
|
|
8
9
|
var uiReact = require('@aws-amplify/ui-react');
|
|
9
10
|
var internal = require('@aws-amplify/ui-react/internal');
|
|
10
|
-
var uiReactCore = require('@aws-amplify/ui-react-core');
|
|
11
11
|
|
|
12
12
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
13
13
|
|
|
@@ -154,6 +154,108 @@ const MessageVariantProvider = ({ children, variant, }) => {
|
|
|
154
154
|
return (React__default["default"].createElement(MessageVariantContext.Provider, { value: variant }, children));
|
|
155
155
|
};
|
|
156
156
|
|
|
157
|
+
function formatDate(date) {
|
|
158
|
+
const dateString = date.toLocaleDateString('en-US', {
|
|
159
|
+
weekday: 'short',
|
|
160
|
+
month: 'short',
|
|
161
|
+
day: 'numeric',
|
|
162
|
+
});
|
|
163
|
+
const timeString = date.toLocaleTimeString('en-US', {
|
|
164
|
+
hour: 'numeric',
|
|
165
|
+
minute: 'numeric',
|
|
166
|
+
hour12: true,
|
|
167
|
+
});
|
|
168
|
+
return `${dateString} at ${timeString}`;
|
|
169
|
+
}
|
|
170
|
+
function arrayBufferToBase64(buffer) {
|
|
171
|
+
let binary = '';
|
|
172
|
+
const bytes = new Uint8Array(buffer);
|
|
173
|
+
const len = bytes.byteLength;
|
|
174
|
+
for (let i = 0; i < len; i++) {
|
|
175
|
+
binary += String.fromCharCode(bytes[i]);
|
|
176
|
+
}
|
|
177
|
+
return window.btoa(binary);
|
|
178
|
+
}
|
|
179
|
+
function convertBufferToBase64(buffer, format) {
|
|
180
|
+
let base64string = '';
|
|
181
|
+
// Use node-based buffer if available
|
|
182
|
+
// fall back on browser if not
|
|
183
|
+
if (typeof Buffer !== 'undefined') {
|
|
184
|
+
base64string = Buffer.from(new Uint8Array(buffer)).toString('base64');
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
base64string = arrayBufferToBase64(buffer);
|
|
188
|
+
}
|
|
189
|
+
return `data:image/${format};base64,${base64string}`;
|
|
190
|
+
}
|
|
191
|
+
function getImageTypeFromMimeType(mimeType) {
|
|
192
|
+
return mimeType.split('/')[1];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const defaultAIConversationDisplayTextEn = {
|
|
196
|
+
getMessageTimestampText: (date) => formatDate(date),
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const { ConversationDisplayTextContext, ConversationDisplayTextProvider, useConversationDisplayText, } = uiReactCore.createContextUtilities({
|
|
200
|
+
contextName: 'ConversationDisplayText',
|
|
201
|
+
defaultValue: defaultAIConversationDisplayTextEn,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const ControlsContext = React__default["default"].createContext(undefined);
|
|
205
|
+
const ControlsProvider = ({ children, controls, }) => {
|
|
206
|
+
return (React__default["default"].createElement(ControlsContext.Provider, { value: controls }, children));
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const LoadingContext = React__default["default"].createContext(undefined);
|
|
210
|
+
const LoadingContextProvider = ({ children, isLoading, }) => {
|
|
211
|
+
return (React__default["default"].createElement(LoadingContext.Provider, { value: isLoading }, children));
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const RESPONSE_COMPONENT_PREFIX = 'AMPLIFY_UI_';
|
|
215
|
+
const ResponseComponentsContext = React__default["default"].createContext(undefined);
|
|
216
|
+
const prependResponseComponents = (responseComponents) => {
|
|
217
|
+
if (!responseComponents)
|
|
218
|
+
return responseComponents;
|
|
219
|
+
return Object.keys(responseComponents).reduce((prev, key) => ((prev[`${RESPONSE_COMPONENT_PREFIX}${key}`] = responseComponents[key]),
|
|
220
|
+
prev), {});
|
|
221
|
+
};
|
|
222
|
+
const ResponseComponentsProvider = ({ children, responseComponents, }) => {
|
|
223
|
+
const _responseComponents = React__default["default"].useMemo(() => prependResponseComponents(responseComponents), [responseComponents]);
|
|
224
|
+
return (React__default["default"].createElement(ResponseComponentsContext.Provider, { value: _responseComponents }, children));
|
|
225
|
+
};
|
|
226
|
+
const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
227
|
+
if (!responseComponents) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const tools = {};
|
|
231
|
+
Object.keys(responseComponents).forEach((toolName) => {
|
|
232
|
+
const { props } = responseComponents[toolName];
|
|
233
|
+
const requiredProps = [];
|
|
234
|
+
Object.keys(props).forEach((propName) => {
|
|
235
|
+
if (props[propName].required)
|
|
236
|
+
requiredProps.push(propName);
|
|
237
|
+
});
|
|
238
|
+
tools[toolName] = {
|
|
239
|
+
description: responseComponents[toolName].description,
|
|
240
|
+
inputSchema: {
|
|
241
|
+
json: {
|
|
242
|
+
type: 'object',
|
|
243
|
+
required: requiredProps,
|
|
244
|
+
properties: {
|
|
245
|
+
...props,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
return { tools };
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const SendMessageContext = React__default["default"].createContext(undefined);
|
|
255
|
+
const SendMessageContextProvider = ({ children, handleSendMessage, }) => {
|
|
256
|
+
return (React__default["default"].createElement(SendMessageContext.Provider, { value: handleSendMessage }, children));
|
|
257
|
+
};
|
|
258
|
+
|
|
157
259
|
const { Button: Button$5, Span: Span$3, View: View$7 } = AIConversationElements;
|
|
158
260
|
const ACTIONS_BAR_BLOCK = 'ai-actions-bar';
|
|
159
261
|
const ActionIcon = elements.withBaseElementProps(Span$3, {
|
|
@@ -370,91 +472,6 @@ const AttachmentListControl = () => {
|
|
|
370
472
|
AttachmentListControl.List = UnorderedList;
|
|
371
473
|
AttachmentListControl.Item = AttachmentControl;
|
|
372
474
|
|
|
373
|
-
const SendMessageContext = React__default["default"].createContext(undefined);
|
|
374
|
-
const SendMessageContextProvider = ({ children, handleSendMessage, }) => {
|
|
375
|
-
return (React__default["default"].createElement(SendMessageContext.Provider, { value: handleSendMessage }, children));
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
const ResponseComponentsContext = React__default["default"].createContext(undefined);
|
|
379
|
-
const ResponseComponentsProvider = ({ children, responseComponents, }) => {
|
|
380
|
-
return (React__default["default"].createElement(ResponseComponentsContext.Provider, { value: responseComponents }, children));
|
|
381
|
-
};
|
|
382
|
-
const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
383
|
-
if (!responseComponents) {
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
const tools = {};
|
|
387
|
-
Object.keys(responseComponents).forEach((toolName) => {
|
|
388
|
-
const { props } = responseComponents[toolName];
|
|
389
|
-
const requiredProps = [];
|
|
390
|
-
Object.keys(props).forEach((propName) => {
|
|
391
|
-
if (props[propName].required)
|
|
392
|
-
requiredProps.push(propName);
|
|
393
|
-
});
|
|
394
|
-
tools[toolName] = {
|
|
395
|
-
description: responseComponents[toolName].description,
|
|
396
|
-
inputSchema: {
|
|
397
|
-
json: {
|
|
398
|
-
type: 'object',
|
|
399
|
-
required: requiredProps,
|
|
400
|
-
properties: {
|
|
401
|
-
...props,
|
|
402
|
-
},
|
|
403
|
-
},
|
|
404
|
-
},
|
|
405
|
-
};
|
|
406
|
-
});
|
|
407
|
-
return { tools };
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const ControlsContext = React__default["default"].createContext(undefined);
|
|
411
|
-
const ControlsProvider = ({ children, controls, }) => {
|
|
412
|
-
return (React__default["default"].createElement(ControlsContext.Provider, { value: controls }, children));
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
function formatDate(date) {
|
|
416
|
-
const dateString = date.toLocaleDateString('en-US', {
|
|
417
|
-
weekday: 'short',
|
|
418
|
-
month: 'short',
|
|
419
|
-
day: 'numeric',
|
|
420
|
-
});
|
|
421
|
-
const timeString = date.toLocaleTimeString('en-US', {
|
|
422
|
-
hour: 'numeric',
|
|
423
|
-
minute: 'numeric',
|
|
424
|
-
hour12: true,
|
|
425
|
-
});
|
|
426
|
-
return `${dateString} at ${timeString}`;
|
|
427
|
-
}
|
|
428
|
-
function arrayBufferToBase64(buffer) {
|
|
429
|
-
let binary = '';
|
|
430
|
-
const bytes = new Uint8Array(buffer);
|
|
431
|
-
const len = bytes.byteLength;
|
|
432
|
-
for (let i = 0; i < len; i++) {
|
|
433
|
-
binary += String.fromCharCode(bytes[i]);
|
|
434
|
-
}
|
|
435
|
-
return window.btoa(binary);
|
|
436
|
-
}
|
|
437
|
-
function convertBufferToBase64(buffer, format) {
|
|
438
|
-
let base64string = '';
|
|
439
|
-
// Use node-based buffer if available
|
|
440
|
-
// fall back on browser if not
|
|
441
|
-
if (typeof Buffer !== 'undefined') {
|
|
442
|
-
base64string = Buffer.from(new Uint8Array(buffer)).toString('base64');
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
base64string = arrayBufferToBase64(buffer);
|
|
446
|
-
}
|
|
447
|
-
return `data:image/${format};base64,${base64string}`;
|
|
448
|
-
}
|
|
449
|
-
function getImageTypeFromMimeType(mimeType) {
|
|
450
|
-
return mimeType.split('/')[1];
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
const LoadingContext = React__default["default"].createContext(undefined);
|
|
454
|
-
const LoadingContextProvider = ({ children, isLoading, }) => {
|
|
455
|
-
return (React__default["default"].createElement(LoadingContext.Provider, { value: isLoading }, children));
|
|
456
|
-
};
|
|
457
|
-
|
|
458
475
|
const { Button: Button$1, Icon: Icon$1, Label: LabelElement, TextArea, View: View$2, } = AIConversationElements;
|
|
459
476
|
const FIELD_BLOCK = 'ai-field';
|
|
460
477
|
const SendIcon = elements.withBaseElementProps(Icon$1, {
|
|
@@ -556,7 +573,7 @@ const FieldControl = () => {
|
|
|
556
573
|
const fileContent = {
|
|
557
574
|
image: {
|
|
558
575
|
format: getImageTypeFromMimeType(file.type),
|
|
559
|
-
source: { bytes: Uint8Array
|
|
576
|
+
source: { bytes: new Uint8Array(buffer) },
|
|
560
577
|
},
|
|
561
578
|
};
|
|
562
579
|
submittedContent.push(fileContent);
|
|
@@ -635,7 +652,9 @@ const MessageControl = ({ message }) => {
|
|
|
635
652
|
else if (content.toolUse) {
|
|
636
653
|
// For now tool use is limited to custom response components
|
|
637
654
|
const { name, input } = content.toolUse;
|
|
638
|
-
if (!responseComponents ||
|
|
655
|
+
if (!responseComponents ||
|
|
656
|
+
!name ||
|
|
657
|
+
!name.startsWith(RESPONSE_COMPONENT_PREFIX)) {
|
|
639
658
|
return;
|
|
640
659
|
}
|
|
641
660
|
else {
|
|
@@ -673,6 +692,7 @@ const Layout = React__default["default"].forwardRef(function Layout(props, ref)
|
|
|
673
692
|
const MessagesControl = ({ renderMessage }) => {
|
|
674
693
|
const messages = React__default["default"].useContext(MessagesContext);
|
|
675
694
|
const controls = React__default["default"].useContext(ControlsContext);
|
|
695
|
+
const { getMessageTimestampText } = useConversationDisplayText();
|
|
676
696
|
const messagesRef = React__default["default"].useRef([]);
|
|
677
697
|
const [focusedItemIndex, setFocusedItemIndex] = React__default["default"].useState(messages ? messages.length - 1 : 0);
|
|
678
698
|
const handleFocus = (index) => setFocusedItemIndex(index);
|
|
@@ -705,13 +725,16 @@ const MessagesControl = ({ renderMessage }) => {
|
|
|
705
725
|
if (controls?.MessageList) {
|
|
706
726
|
return React__default["default"].createElement(controls.MessageList, { messages: messages });
|
|
707
727
|
}
|
|
708
|
-
|
|
728
|
+
const messagesWithRenderableContent = messages?.filter((message) => message.content.some((content) => content.image ??
|
|
729
|
+
content.text ??
|
|
730
|
+
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
731
|
+
return (React__default["default"].createElement(Layout, null, messagesWithRenderableContent?.map((message, index) => {
|
|
709
732
|
return renderMessage ? (renderMessage(message)) : (React__default["default"].createElement(RoleContext.Provider, { value: message.role, key: `message-${index}` },
|
|
710
733
|
React__default["default"].createElement(MessageContainer, { "data-testid": `message`, key: `message-${index}`, tabIndex: focusedItemIndex === index ? 0 : -1, onFocus: () => handleFocus(index), onKeyDown: (event) => onKeyDown(index, event), ref: (el) => (messagesRef.current[index] = el) },
|
|
711
734
|
React__default["default"].createElement(HeaderContainer, null,
|
|
712
735
|
React__default["default"].createElement(AvatarControl, null),
|
|
713
736
|
React__default["default"].createElement(Separator, null),
|
|
714
|
-
React__default["default"].createElement(Timestamp, null,
|
|
737
|
+
React__default["default"].createElement(Timestamp, null, getMessageTimestampText(new Date(message.createdAt)))),
|
|
715
738
|
React__default["default"].createElement(MessageControl, { message: message }),
|
|
716
739
|
message.role === 'assistant' ? (React__default["default"].createElement(ActionsBarControl, { message: message, focusable: focusedItemIndex === index })) : null)));
|
|
717
740
|
})));
|
|
@@ -807,19 +830,24 @@ function Conversation() {
|
|
|
807
830
|
React__default["default"].createElement(FieldControl, null))));
|
|
808
831
|
}
|
|
809
832
|
|
|
810
|
-
function createProvider({ elements: elements$1, actions, suggestedPrompts, responseComponents, variant, controls, }) {
|
|
833
|
+
function createProvider({ elements: elements$1, actions, suggestedPrompts, responseComponents, variant, controls, displayText, }) {
|
|
811
834
|
return function Provider({ children, messages, avatars, handleSendMessage, isLoading, }) {
|
|
835
|
+
const _displayText = {
|
|
836
|
+
...defaultAIConversationDisplayTextEn,
|
|
837
|
+
...displayText,
|
|
838
|
+
};
|
|
812
839
|
return (React__default["default"].createElement(elements.ElementsProvider, { elements: elements$1 },
|
|
813
840
|
React__default["default"].createElement(ControlsProvider, { controls: controls },
|
|
814
841
|
React__default["default"].createElement(SuggestedPromptProvider, { suggestedPrompts: suggestedPrompts },
|
|
815
842
|
React__default["default"].createElement(ResponseComponentsProvider, { responseComponents: responseComponents },
|
|
816
|
-
React__default["default"].createElement(
|
|
817
|
-
React__default["default"].createElement(
|
|
818
|
-
React__default["default"].createElement(
|
|
819
|
-
React__default["default"].createElement(
|
|
820
|
-
React__default["default"].createElement(
|
|
821
|
-
React__default["default"].createElement(
|
|
822
|
-
React__default["default"].createElement(
|
|
843
|
+
React__default["default"].createElement(ConversationDisplayTextProvider, { ..._displayText },
|
|
844
|
+
React__default["default"].createElement(ConversationInputContextProvider, null,
|
|
845
|
+
React__default["default"].createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
|
|
846
|
+
React__default["default"].createElement(AvatarsProvider, { avatars: avatars },
|
|
847
|
+
React__default["default"].createElement(ActionsProvider, { actions: actions },
|
|
848
|
+
React__default["default"].createElement(MessageVariantProvider, { variant: variant },
|
|
849
|
+
React__default["default"].createElement(MessagesProvider, { messages: messages },
|
|
850
|
+
React__default["default"].createElement(LoadingContextProvider, { isLoading: isLoading }, children)))))))))))));
|
|
823
851
|
};
|
|
824
852
|
}
|
|
825
853
|
|
|
@@ -827,7 +855,7 @@ function createProvider({ elements: elements$1, actions, suggestedPrompts, respo
|
|
|
827
855
|
* @experimental
|
|
828
856
|
*/
|
|
829
857
|
function createAIConversation(input = {}) {
|
|
830
|
-
const { elements, suggestedPrompts, actions, responseComponents, variant, controls, } = input;
|
|
858
|
+
const { elements, suggestedPrompts, actions, responseComponents, variant, controls, displayText, } = input;
|
|
831
859
|
const Provider = createProvider({
|
|
832
860
|
elements,
|
|
833
861
|
actions,
|
|
@@ -835,6 +863,7 @@ function createAIConversation(input = {}) {
|
|
|
835
863
|
responseComponents,
|
|
836
864
|
variant,
|
|
837
865
|
controls,
|
|
866
|
+
displayText,
|
|
838
867
|
});
|
|
839
868
|
function AIConversation(props) {
|
|
840
869
|
const { messages, avatars, handleSendMessage, isLoading } = props;
|
|
@@ -859,11 +888,23 @@ const MessageMeta = ({ message }) => {
|
|
|
859
888
|
// need to pass this in as props in order for it to be overridable
|
|
860
889
|
const avatars = React__namespace.useContext(AvatarsContext);
|
|
861
890
|
const role = React__namespace.useContext(RoleContext);
|
|
891
|
+
const { getMessageTimestampText } = useConversationDisplayText();
|
|
862
892
|
// maybe rename 'avatar' to something else
|
|
863
893
|
const avatar = role === 'assistant' ? avatars?.ai : avatars?.user;
|
|
864
894
|
return (React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageSender },
|
|
865
895
|
React__namespace.createElement(uiReact.Text, { className: ui.ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username),
|
|
866
|
-
React__namespace.createElement(uiReact.Text, { className: ui.ComponentClassName.AIConversationMessageSenderTimestamp },
|
|
896
|
+
React__namespace.createElement(uiReact.Text, { className: ui.ComponentClassName.AIConversationMessageSenderTimestamp }, getMessageTimestampText(new Date(message.createdAt)))));
|
|
897
|
+
};
|
|
898
|
+
const LoadingMessage = () => {
|
|
899
|
+
const avatars = React__namespace.useContext(AvatarsContext);
|
|
900
|
+
const variant = React__namespace.useContext(MessageVariantContext);
|
|
901
|
+
const avatar = avatars?.ai;
|
|
902
|
+
return (React__namespace.createElement(uiReact.View, { className: ui.classNames(ui.ComponentClassName.AIConversationMessage, ui.classNameModifier(ui.ComponentClassName.AIConversationMessage, variant), ui.classNameModifier(ui.ComponentClassName.AIConversationMessage, 'assistant')) },
|
|
903
|
+
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageAvatar },
|
|
904
|
+
React__namespace.createElement(uiReact.Avatar, { isLoading: true }, avatar?.avatar)),
|
|
905
|
+
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageBody },
|
|
906
|
+
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageSender },
|
|
907
|
+
React__namespace.createElement(uiReact.Text, { className: ui.ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username)))));
|
|
867
908
|
};
|
|
868
909
|
const Message = ({ message }) => {
|
|
869
910
|
const avatars = React__namespace.useContext(AvatarsContext);
|
|
@@ -879,7 +920,13 @@ const Message = ({ message }) => {
|
|
|
879
920
|
React__namespace.createElement(MessageControl, { message: message }))))));
|
|
880
921
|
};
|
|
881
922
|
const MessageList = ({ messages, }) => {
|
|
882
|
-
|
|
923
|
+
const isLoading = React__namespace.useContext(LoadingContext);
|
|
924
|
+
const messagesWithRenderableContent = messages?.filter((message) => message.content.some((content) => content.image ??
|
|
925
|
+
content.text ??
|
|
926
|
+
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
927
|
+
return (React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageList },
|
|
928
|
+
messagesWithRenderableContent.map((message, i) => (React__namespace.createElement(Message, { key: `message-${i}`, message: message }))),
|
|
929
|
+
isLoading ? React__namespace.createElement(LoadingMessage, null) : null));
|
|
883
930
|
};
|
|
884
931
|
|
|
885
932
|
const Attachment = ({ file, handleRemove, }) => {
|
|
@@ -924,6 +971,9 @@ const Form = ({ setInput, input, handleSubmit, }) => {
|
|
|
924
971
|
React__namespace.createElement(uiReact.View, { as: "form", className: ui.ComponentClassName.AIConversationForm, onSubmit: handleSubmit },
|
|
925
972
|
React__namespace.createElement(uiReact.Button, { className: ui.ComponentClassName.AIConversationFormAttach, onClick: () => {
|
|
926
973
|
hiddenInput?.current?.click();
|
|
974
|
+
if (hiddenInput?.current) {
|
|
975
|
+
hiddenInput.current.value = '';
|
|
976
|
+
}
|
|
927
977
|
} },
|
|
928
978
|
React__namespace.createElement("span", null, attachIcon),
|
|
929
979
|
React__namespace.createElement(uiReact.VisuallyHidden, null,
|
|
@@ -936,8 +986,8 @@ const Form = ({ setInput, input, handleSubmit, }) => {
|
|
|
936
986
|
...prevValue,
|
|
937
987
|
files: [...(prevValue?.files ?? []), ...Array.from(files)],
|
|
938
988
|
}));
|
|
939
|
-
}, multiple: true, accept: "*" }))),
|
|
940
|
-
React__namespace.createElement(uiReact.TextAreaField, { className: ui.ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', onKeyDown: (e) => {
|
|
989
|
+
}, multiple: true, accept: "*", "data-testid": "hidden-file-input" }))),
|
|
990
|
+
React__namespace.createElement(uiReact.TextAreaField, { className: ui.ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', testId: "text-input", onKeyDown: (e) => {
|
|
941
991
|
// Submit on enter key if shift is not pressed also
|
|
942
992
|
const shouldSubmit = !e.shiftKey && e.key === 'Enter';
|
|
943
993
|
if (shouldSubmit && isHTMLFormElement(e.target)) {
|
|
@@ -969,7 +1019,7 @@ const PromptList = ({ setInput, suggestedPrompts = [], }) => {
|
|
|
969
1019
|
})));
|
|
970
1020
|
};
|
|
971
1021
|
|
|
972
|
-
function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, }) {
|
|
1022
|
+
function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, displayText, }) {
|
|
973
1023
|
const icons = internal.useIcons('aiConversation');
|
|
974
1024
|
const defaultAvatars = {
|
|
975
1025
|
ai: {
|
|
@@ -997,6 +1047,7 @@ function AIConversationBase({ actions, avatars, controls, handleSendMessage, mes
|
|
|
997
1047
|
Form,
|
|
998
1048
|
...controls,
|
|
999
1049
|
},
|
|
1050
|
+
displayText,
|
|
1000
1051
|
});
|
|
1001
1052
|
const providerProps = {
|
|
1002
1053
|
messages,
|
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { AIConversationInput, AIConversationProps } from './types';
|
|
3
3
|
interface AIConversationBaseProps extends AIConversationProps, AIConversationInput {
|
|
4
4
|
}
|
|
5
|
-
declare function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, }: AIConversationBaseProps): JSX.Element;
|
|
5
|
+
declare function AIConversationBase({ actions, avatars, controls, handleSendMessage, messages, responseComponents, suggestedPrompts, variant, isLoading, displayText, }: AIConversationBaseProps): JSX.Element;
|
|
6
6
|
/**
|
|
7
7
|
* @experimental
|
|
8
8
|
*/
|
|
@@ -12,10 +12,10 @@ export declare const AIConversation: typeof AIConversationBase & {
|
|
|
12
12
|
}> | undefined;
|
|
13
13
|
PromptList: React.ComponentType<{
|
|
14
14
|
suggestedPrompts?: import("./types").SuggestedPrompt[] | undefined;
|
|
15
|
-
setInput: React.Dispatch<React.SetStateAction<import("./context
|
|
15
|
+
setInput: React.Dispatch<React.SetStateAction<import("./context").ConversationInput | undefined>> | undefined;
|
|
16
16
|
}> | undefined;
|
|
17
|
-
Form: React.ComponentType<{
|
|
17
|
+
Form: NonNullable<React.ComponentType<{
|
|
18
18
|
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
|
|
19
|
-
} & Required<import("./context").ConversationInputContext>> | undefined
|
|
19
|
+
} & Required<import("./context").ConversationInputContext>> | undefined>;
|
|
20
20
|
};
|
|
21
21
|
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { ConversationDisplayText } from '../displayText';
|
|
3
|
+
export declare const ConversationDisplayTextContext: import("react").Context<Required<ConversationDisplayText> | undefined>, ConversationDisplayTextProvider: import("react").ComponentType<import("react").PropsWithChildren<Required<ConversationDisplayText>>>, useConversationDisplayText: (params?: {
|
|
4
|
+
errorMessage?: string | undefined;
|
|
5
|
+
} | undefined) => Required<ConversationDisplayText>;
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { ResponseComponents } from '../types';
|
|
3
3
|
import { ToolConfiguration } from '../../../types';
|
|
4
4
|
type ResponseComponentsContextProps = ResponseComponents | undefined;
|
|
5
|
+
export declare const RESPONSE_COMPONENT_PREFIX = "AMPLIFY_UI_";
|
|
5
6
|
export declare const ResponseComponentsContext: React.Context<ResponseComponentsContextProps>;
|
|
6
7
|
export declare const ResponseComponentsProvider: ({ children, responseComponents, }: {
|
|
7
8
|
children?: React.ReactNode;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export {
|
|
1
|
+
export { ActionsContext, ActionsProvider } from './ActionsContext';
|
|
2
|
+
export { AvatarsContext, AvatarsProvider } from './AvatarsContext';
|
|
3
|
+
export { ConversationInputContext, ConversationInput, ConversationInputContextProvider, } from './ConversationInputContext';
|
|
4
|
+
export { MessagesContext, RoleContext, MessagesProvider, } from './MessagesContext';
|
|
5
|
+
export { SuggestedPromptsContext, SuggestedPromptProvider, } from './SuggestedPromptsContext';
|
|
6
|
+
export { MessageVariantContext, MessageVariantProvider, } from './MessageVariantContext';
|
|
7
|
+
export { ConversationDisplayTextContext, useConversationDisplayText, ConversationDisplayTextProvider, } from './DisplayTextContext';
|
|
8
|
+
export { ControlsContext, ControlsContextProps, ControlsProvider, } from './ControlsContext';
|
|
9
|
+
export { LoadingContextProvider } from './LoadingContext';
|
|
10
|
+
export { ResponseComponentsProvider, RESPONSE_COMPONENT_PREFIX, } from './ResponseComponentsContext';
|
|
11
|
+
export { SendMessageContextProvider } from './SendMessageContext';
|
|
8
12
|
export * from './elements';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { AIConversationInput, AIConversationProps } from './types';
|
|
3
|
-
export default function createProvider({ elements, actions, suggestedPrompts, responseComponents, variant, controls, }: Pick<AIConversationInput, 'elements' | 'actions' | 'suggestedPrompts' | 'responseComponents' | 'variant' | 'controls'>): ({ children, messages, avatars, handleSendMessage, isLoading, }: {
|
|
3
|
+
export default function createProvider({ elements, actions, suggestedPrompts, responseComponents, variant, controls, displayText, }: Pick<AIConversationInput, 'elements' | 'actions' | 'suggestedPrompts' | 'responseComponents' | 'variant' | 'controls' | 'displayText'>): ({ children, messages, avatars, handleSendMessage, isLoading, }: {
|
|
4
4
|
children?: React.ReactNode;
|
|
5
|
-
} & Pick<AIConversationProps, "avatars" | "messages" | "
|
|
5
|
+
} & Pick<AIConversationProps, "avatars" | "messages" | "isLoading" | "handleSendMessage">) => React.JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DisplayTextTemplate } from '@aws-amplify/ui';
|
|
2
2
|
export type ConversationDisplayText = {
|
|
3
|
-
|
|
3
|
+
getMessageTimestampText?: (date: Date) => string;
|
|
4
4
|
};
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const defaultAIConversationDisplayTextEn: Required<AIConversationDisplayText>;
|
|
6
6
|
export type AIConversationDisplayText = DisplayTextTemplate<ConversationDisplayText>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { createAIConversation } from './createAIConversation';
|
|
2
2
|
import { AIConversation } from './AIConversation';
|
|
3
|
-
|
|
3
|
+
import { Avatars, CustomAction, ResponseComponent, SuggestedPrompt } from './types';
|
|
4
|
+
export { createAIConversation, AIConversation, Avatars, CustomAction, ResponseComponent, SuggestedPrompt, };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { ControlsContextProps } from '../../context/ControlsContext';
|
|
2
|
-
export declare const Form: ControlsContextProps['Form']
|
|
2
|
+
export declare const Form: NonNullable<ControlsContextProps['Form']>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export {
|
|
1
|
+
export { createAIConversation, AIConversation, Avatars, CustomAction, ResponseComponent, SuggestedPrompt, } from './components';
|
|
2
|
+
export { createAIHooks, AIContextProvider } from './hooks';
|
|
3
|
+
export { ConversationMessage, ConversationMessageContent, SendMessage, SendMesageParameters, } from './types';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/ui-react-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/esm/index.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"react-dom": "^16.14.0 || ^17.0 || ^18.0"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@aws-amplify/ui": "^6.
|
|
51
|
-
"@aws-amplify/ui-react": "^6.
|
|
52
|
-
"@aws-amplify/ui-react-core": "^3.0.
|
|
50
|
+
"@aws-amplify/ui": "^6.5.0",
|
|
51
|
+
"@aws-amplify/ui-react": "^6.4.0",
|
|
52
|
+
"@aws-amplify/ui-react-core": "^3.0.23"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@rollup/plugin-commonjs": "^22.0.1",
|