@aws-amplify/ui-react-ai 0.4.0 → 1.1.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 +8 -26
- package/dist/esm/components/AIConversation/AIConversationProvider.mjs +20 -17
- package/dist/esm/components/AIConversation/context/AIContextContext.mjs +8 -0
- package/dist/esm/components/AIConversation/context/AttachmentContext.mjs +12 -3
- package/dist/esm/components/AIConversation/context/ConversationInputContext.mjs +2 -1
- package/dist/esm/components/AIConversation/context/FallbackComponentContext.mjs +8 -0
- package/dist/esm/components/AIConversation/context/ResponseComponentsContext.mjs +6 -2
- package/dist/esm/components/AIConversation/context/elements/IconElement.mjs +2 -2
- package/dist/esm/components/AIConversation/context/elements/definitions.mjs +12 -12
- package/dist/esm/components/AIConversation/createAIConversation.mjs +2 -5
- package/dist/esm/components/AIConversation/displayText.mjs +6 -0
- package/dist/esm/components/AIConversation/utils.mjs +42 -13
- package/dist/esm/components/AIConversation/views/Controls/ActionsBarControl.mjs +3 -2
- package/dist/esm/components/AIConversation/views/Controls/AttachFileControl.mjs +2 -0
- package/dist/esm/components/AIConversation/views/Controls/AttachmentListControl.mjs +2 -0
- package/dist/esm/components/AIConversation/views/Controls/AvatarControl.mjs +2 -0
- package/dist/esm/components/AIConversation/views/Controls/DefaultMessageControl.mjs +2 -0
- package/dist/esm/components/AIConversation/views/Controls/FormControl.mjs +44 -8
- package/dist/esm/components/AIConversation/views/Controls/MessagesControl.mjs +24 -31
- package/dist/esm/components/AIConversation/views/Controls/PromptControl.mjs +2 -0
- package/dist/esm/components/AIConversation/views/default/Form.mjs +13 -20
- package/dist/esm/components/AIConversation/views/default/MessageList.mjs +31 -16
- package/dist/esm/hooks/contentFromEvents.mjs +22 -0
- package/dist/esm/hooks/createAIHooks.mjs +0 -3
- package/dist/esm/hooks/exhaustivelyListMessages.mjs +19 -0
- package/dist/esm/hooks/shared.mjs +14 -0
- package/dist/esm/hooks/useAIConversation.mjs +246 -106
- package/dist/esm/hooks/useAIGeneration.mjs +1 -8
- package/dist/esm/index.mjs +0 -1
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +508 -280
- package/dist/types/components/AIConversation/AIConversation.d.ts +0 -3
- package/dist/types/components/AIConversation/AIConversationProvider.d.ts +1 -1
- package/dist/types/components/AIConversation/context/AIContextContext.d.ts +6 -0
- package/dist/types/components/AIConversation/context/AttachmentContext.d.ts +5 -5
- package/dist/types/components/AIConversation/context/ControlsContext.d.ts +5 -3
- package/dist/types/components/AIConversation/context/ConversationInputContext.d.ts +4 -2
- package/dist/types/components/AIConversation/context/DisplayTextContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/FallbackComponentContext.d.ts +7 -0
- package/dist/types/components/AIConversation/context/MessageRenderContext.d.ts +1 -1
- package/dist/types/components/AIConversation/context/ResponseComponentsContext.d.ts +2 -2
- package/dist/types/components/AIConversation/context/elements/IconElement.d.ts +2 -2
- package/dist/types/components/AIConversation/context/elements/definitions.d.ts +12 -12
- package/dist/types/components/AIConversation/context/index.d.ts +4 -2
- package/dist/types/components/AIConversation/createAIConversation.d.ts +0 -3
- package/dist/types/components/AIConversation/displayText.d.ts +2 -0
- package/dist/types/components/AIConversation/index.d.ts +2 -1
- package/dist/types/components/AIConversation/types.d.ts +6 -24
- package/dist/types/components/AIConversation/utils.d.ts +10 -0
- package/dist/types/components/AIConversation/views/Controls/MessagesControl.d.ts +1 -5
- package/dist/types/components/AIConversation/views/default/Attachments.d.ts +2 -2
- package/dist/types/components/AIConversation/views/default/Form.d.ts +1 -1
- package/dist/types/components/AIConversation/views/default/MessageList.d.ts +1 -1
- package/dist/types/components/AIConversation/views/default/PromptList.d.ts +1 -1
- package/dist/types/hooks/contentFromEvents.d.ts +2 -0
- package/dist/types/hooks/createAIHooks.d.ts +0 -3
- package/dist/types/hooks/exhaustivelyListMessages.d.ts +8 -0
- package/dist/types/hooks/index.d.ts +1 -2
- package/dist/types/hooks/shared.d.ts +23 -0
- package/dist/types/hooks/useAIConversation.d.ts +6 -4
- package/dist/types/hooks/useAIGeneration.d.ts +3 -13
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types.d.ts +32 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +6 -6
- package/dist/ai-conversation-styles.css +0 -195
- package/dist/ai-conversation-styles.js +0 -2
- package/dist/esm/hooks/AIContextProvider.mjs +0 -20
- package/dist/types/ai-conversation-styles.d.ts +0 -1
- package/dist/types/hooks/AIContextProvider.d.ts +0 -17
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Flex, ScrollView } from '@aws-amplify/ui-react';
|
|
3
3
|
import { useIcons, IconAssistant, IconUser } from '@aws-amplify/ui-react/internal';
|
|
4
4
|
import { MessagesControl } from './views/Controls/MessagesControl.mjs';
|
|
5
5
|
import { FormControl } from './views/Controls/FormControl.mjs';
|
|
@@ -12,7 +12,7 @@ import { useSetUserAgent } from '@aws-amplify/ui-react-core';
|
|
|
12
12
|
import { VERSION } from '../../version.mjs';
|
|
13
13
|
import { DefaultMessageControl } from './views/Controls/DefaultMessageControl.mjs';
|
|
14
14
|
|
|
15
|
-
function
|
|
15
|
+
function AIConversationBase({ avatars, controls, ...rest }) {
|
|
16
16
|
useSetUserAgent({
|
|
17
17
|
componentName: 'AIConversation',
|
|
18
18
|
packageName: 'react-ai',
|
|
@@ -22,53 +22,35 @@ function Provider({ actions, avatars, controls, handleSendMessage, messages, res
|
|
|
22
22
|
const defaultAvatars = {
|
|
23
23
|
ai: {
|
|
24
24
|
username: 'Assistant',
|
|
25
|
-
avatar: icons?.assistant ?? React.createElement(IconAssistant,
|
|
25
|
+
avatar: icons?.assistant ?? React.createElement(IconAssistant, { testId: "icon-assistant" }),
|
|
26
26
|
},
|
|
27
27
|
user: {
|
|
28
28
|
username: 'User',
|
|
29
|
-
avatar: icons?.user ?? React.createElement(IconUser,
|
|
29
|
+
avatar: icons?.user ?? React.createElement(IconUser, { testId: "icon-user" }),
|
|
30
30
|
},
|
|
31
31
|
};
|
|
32
32
|
const providerProps = {
|
|
33
|
-
|
|
34
|
-
handleSendMessage,
|
|
33
|
+
...rest,
|
|
35
34
|
avatars: {
|
|
36
35
|
...defaultAvatars,
|
|
37
36
|
...avatars,
|
|
38
37
|
},
|
|
39
|
-
isLoading,
|
|
40
|
-
elements: {
|
|
41
|
-
Text,
|
|
42
|
-
},
|
|
43
|
-
actions,
|
|
44
|
-
suggestedPrompts,
|
|
45
|
-
responseComponents,
|
|
46
|
-
variant,
|
|
47
38
|
controls: {
|
|
48
39
|
MessageList,
|
|
49
40
|
PromptList,
|
|
50
41
|
Form,
|
|
51
42
|
...controls,
|
|
52
43
|
},
|
|
53
|
-
displayText,
|
|
54
|
-
allowAttachments,
|
|
55
|
-
messageRenderer,
|
|
56
44
|
};
|
|
57
|
-
return (React.createElement(AIConversationProvider, { ...providerProps },
|
|
58
|
-
}
|
|
59
|
-
function AIConversationBase(props) {
|
|
60
|
-
return (React.createElement(Provider, { ...props },
|
|
61
|
-
React.createElement(Flex, { className: ComponentClassName.AIConversation },
|
|
45
|
+
return (React.createElement(AIConversationProvider, { ...providerProps },
|
|
46
|
+
React.createElement(Flex, { className: ComponentClassName.AIConversation, testId: "ai-conversation" },
|
|
62
47
|
React.createElement(ScrollView, { autoScroll: "smooth", flex: "1" },
|
|
63
48
|
React.createElement(DefaultMessageControl, null),
|
|
64
49
|
React.createElement(MessagesControl, null)),
|
|
65
50
|
React.createElement(FormControl, null))));
|
|
66
51
|
}
|
|
67
|
-
/**
|
|
68
|
-
* @experimental
|
|
69
|
-
*/
|
|
70
52
|
const AIConversation = Object.assign(AIConversationBase, {
|
|
71
|
-
Provider,
|
|
53
|
+
Provider: AIConversationProvider,
|
|
72
54
|
DefaultMessage: DefaultMessageControl,
|
|
73
55
|
Messages: MessagesControl,
|
|
74
56
|
Form: FormControl,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
|
-
import { ElementsProvider } from '@aws-amplify/ui-react-core/elements';
|
|
3
2
|
import { defaultAIConversationDisplayTextEn } from './displayText.mjs';
|
|
3
|
+
import { AIContextProvider } from './context/AIContextContext.mjs';
|
|
4
4
|
import { ActionsProvider } from './context/ActionsContext.mjs';
|
|
5
5
|
import { AvatarsProvider } from './context/AvatarsContext.mjs';
|
|
6
6
|
import { ConversationInputContextProvider } from './context/ConversationInputContext.mjs';
|
|
@@ -12,30 +12,33 @@ import { ControlsProvider } from './context/ControlsContext.mjs';
|
|
|
12
12
|
import { LoadingContextProvider } from './context/LoadingContext.mjs';
|
|
13
13
|
import { ResponseComponentsProvider } from './context/ResponseComponentsContext.mjs';
|
|
14
14
|
import { SendMessageContextProvider } from './context/SendMessageContext.mjs';
|
|
15
|
-
import './context/MessageRenderContext.mjs';
|
|
15
|
+
import { MessageRendererProvider } from './context/MessageRenderContext.mjs';
|
|
16
16
|
import { AttachmentProvider } from './context/AttachmentContext.mjs';
|
|
17
17
|
import { WelcomeMessageProvider } from './context/WelcomeMessageContext.mjs';
|
|
18
|
+
import { FallbackComponentProvider } from './context/FallbackComponentContext.mjs';
|
|
18
19
|
import './context/elements/definitions.mjs';
|
|
19
20
|
|
|
20
|
-
const AIConversationProvider = ({ actions, allowAttachments, avatars, children, controls, displayText,
|
|
21
|
+
const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, maxAttachmentSize, maxAttachments, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }) => {
|
|
21
22
|
const _displayText = {
|
|
22
23
|
...defaultAIConversationDisplayTextEn,
|
|
23
24
|
...displayText,
|
|
24
25
|
};
|
|
25
|
-
return (React__default.createElement(
|
|
26
|
-
React__default.createElement(
|
|
27
|
-
React__default.createElement(
|
|
28
|
-
React__default.createElement(
|
|
29
|
-
React__default.createElement(
|
|
30
|
-
React__default.createElement(
|
|
31
|
-
React__default.createElement(
|
|
32
|
-
React__default.createElement(
|
|
33
|
-
React__default.createElement(
|
|
34
|
-
React__default.createElement(
|
|
35
|
-
React__default.createElement(
|
|
36
|
-
React__default.createElement(
|
|
37
|
-
React__default.createElement(
|
|
38
|
-
React__default.createElement(
|
|
26
|
+
return (React__default.createElement(ControlsProvider, { controls: controls },
|
|
27
|
+
React__default.createElement(SuggestedPromptProvider, { suggestedPrompts: suggestedPrompts },
|
|
28
|
+
React__default.createElement(WelcomeMessageProvider, { welcomeMessage: welcomeMessage },
|
|
29
|
+
React__default.createElement(FallbackComponentProvider, { FallbackComponent: FallbackResponseComponent },
|
|
30
|
+
React__default.createElement(MessageRendererProvider, { ...messageRenderer },
|
|
31
|
+
React__default.createElement(ResponseComponentsProvider, { responseComponents: responseComponents },
|
|
32
|
+
React__default.createElement(AttachmentProvider, { allowAttachments: allowAttachments, maxAttachmentSize: maxAttachmentSize, maxAttachments: maxAttachments },
|
|
33
|
+
React__default.createElement(ConversationDisplayTextProvider, { ..._displayText },
|
|
34
|
+
React__default.createElement(ConversationInputContextProvider, null,
|
|
35
|
+
React__default.createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
|
|
36
|
+
React__default.createElement(AvatarsProvider, { avatars: avatars },
|
|
37
|
+
React__default.createElement(ActionsProvider, { actions: actions },
|
|
38
|
+
React__default.createElement(MessageVariantProvider, { variant: variant },
|
|
39
|
+
React__default.createElement(MessagesProvider, { messages: messages },
|
|
40
|
+
React__default.createElement(AIContextProvider, { aiContext: aiContext },
|
|
41
|
+
React__default.createElement(LoadingContextProvider, { isLoading: isLoading }, children)))))))))))))))));
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
export { AIConversationProvider };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
|
|
3
|
+
const AIContextContext = React__default.createContext(undefined);
|
|
4
|
+
const AIContextProvider = ({ children, aiContext, }) => {
|
|
5
|
+
return (React__default.createElement(AIContextContext.Provider, { value: aiContext }, children));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export { AIContextContext, AIContextProvider };
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
const AttachmentContext = React.createContext(
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const AttachmentContext = React.createContext({
|
|
4
|
+
allowAttachments: false,
|
|
5
|
+
// We save attachments as base64 strings into dynamodb for conversation history
|
|
6
|
+
// DynamoDB has a max size of 400kb for records
|
|
7
|
+
// This can be overridden so cutsomers could provide a lower number
|
|
8
|
+
// or a higher number if in the future we support larger sizes.
|
|
9
|
+
maxAttachmentSize: 400000,
|
|
10
|
+
maxAttachments: 20,
|
|
11
|
+
});
|
|
12
|
+
const AttachmentProvider = ({ children, allowAttachments = false, maxAttachmentSize = 400000, maxAttachments = 20, }) => {
|
|
13
|
+
const providerValue = React.useMemo(() => ({ maxAttachmentSize, maxAttachments, allowAttachments }), [maxAttachmentSize, maxAttachments, allowAttachments]);
|
|
14
|
+
return (React.createElement(AttachmentContext.Provider, { value: providerValue }, children));
|
|
6
15
|
};
|
|
7
16
|
|
|
8
17
|
export { AttachmentContext, AttachmentProvider };
|
|
@@ -3,7 +3,8 @@ import React__default from 'react';
|
|
|
3
3
|
const ConversationInputContext = React__default.createContext({});
|
|
4
4
|
const ConversationInputContextProvider = ({ children, }) => {
|
|
5
5
|
const [input, setInput] = React__default.useState();
|
|
6
|
-
const
|
|
6
|
+
const [error, setError] = React__default.useState();
|
|
7
|
+
const providerValue = React__default.useMemo(() => ({ input, setInput, error, setError }), [input, setInput, error, setError]);
|
|
7
8
|
return (React__default.createElement(ConversationInputContext.Provider, { value: providerValue }, children));
|
|
8
9
|
};
|
|
9
10
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
|
|
3
|
+
const FallbackComponentContext = React__default.createContext(undefined);
|
|
4
|
+
const FallbackComponentProvider = ({ children, FallbackComponent, }) => {
|
|
5
|
+
return (React__default.createElement(FallbackComponentContext.Provider, { value: FallbackComponent }, children));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export { FallbackComponentContext, FallbackComponentProvider };
|
|
@@ -21,8 +21,12 @@ const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
|
21
21
|
const { props } = responseComponents[toolName];
|
|
22
22
|
const requiredProps = [];
|
|
23
23
|
Object.keys(props).forEach((propName) => {
|
|
24
|
-
if (props[propName].required)
|
|
24
|
+
if (props[propName].required) {
|
|
25
25
|
requiredProps.push(propName);
|
|
26
|
+
// The inputSchema for a tool needs to not
|
|
27
|
+
// have `required` in the properties
|
|
28
|
+
props[propName].required = undefined;
|
|
29
|
+
}
|
|
26
30
|
});
|
|
27
31
|
tools[toolName] = {
|
|
28
32
|
description: responseComponents[toolName].description,
|
|
@@ -40,4 +44,4 @@ const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
|
40
44
|
return { tools };
|
|
41
45
|
};
|
|
42
46
|
|
|
43
|
-
export { RESPONSE_COMPONENT_PREFIX, ResponseComponentsContext, ResponseComponentsProvider, convertResponseComponentsToToolConfiguration };
|
|
47
|
+
export { RESPONSE_COMPONENT_PREFIX, ResponseComponentsContext, ResponseComponentsProvider, convertResponseComponentsToToolConfiguration, prependResponseComponents };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineBaseElementWithRef, withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
|
|
2
2
|
import React__default from 'react';
|
|
3
3
|
|
|
4
4
|
const DEFAULT_ICON_PATHS = {
|
|
@@ -17,7 +17,7 @@ const DEFAULT_ICON_ATTRIBUTES = {
|
|
|
17
17
|
fill: 'none',
|
|
18
18
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
19
19
|
};
|
|
20
|
-
const BaseIconElement =
|
|
20
|
+
const BaseIconElement = defineBaseElementWithRef({
|
|
21
21
|
type: 'svg',
|
|
22
22
|
displayName: 'Icon',
|
|
23
23
|
});
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineBaseElementWithRef } from '@aws-amplify/ui-react-core/elements';
|
|
2
2
|
import { IconElement } from './IconElement.mjs';
|
|
3
3
|
|
|
4
|
-
const LabelElement =
|
|
4
|
+
const LabelElement = defineBaseElementWithRef({
|
|
5
5
|
type: 'label',
|
|
6
6
|
displayName: 'Label',
|
|
7
7
|
});
|
|
8
|
-
const TextElement =
|
|
8
|
+
const TextElement = defineBaseElementWithRef({
|
|
9
9
|
type: 'p',
|
|
10
10
|
displayName: 'Text',
|
|
11
11
|
});
|
|
12
|
-
const UnorderedListElement =
|
|
12
|
+
const UnorderedListElement = defineBaseElementWithRef({
|
|
13
13
|
type: 'ul',
|
|
14
14
|
displayName: 'UnorderedList',
|
|
15
15
|
});
|
|
16
|
-
const ListItemElement =
|
|
16
|
+
const ListItemElement = defineBaseElementWithRef({
|
|
17
17
|
type: 'li',
|
|
18
18
|
displayName: 'ListItem',
|
|
19
19
|
});
|
|
20
|
-
const HeadingElement =
|
|
20
|
+
const HeadingElement = defineBaseElementWithRef({
|
|
21
21
|
type: 'h2',
|
|
22
22
|
displayName: 'Title',
|
|
23
23
|
});
|
|
24
|
-
const ImageElement =
|
|
24
|
+
const ImageElement = defineBaseElementWithRef({
|
|
25
25
|
type: 'img',
|
|
26
26
|
displayName: 'Image',
|
|
27
27
|
});
|
|
28
|
-
const InputElement =
|
|
28
|
+
const InputElement = defineBaseElementWithRef({
|
|
29
29
|
type: 'input',
|
|
30
30
|
displayName: 'Input',
|
|
31
31
|
});
|
|
32
|
-
const ButtonElement =
|
|
33
|
-
const ViewElement =
|
|
32
|
+
const ButtonElement = defineBaseElementWithRef({ type: 'button', displayName: 'Button' });
|
|
33
|
+
const ViewElement = defineBaseElementWithRef({
|
|
34
34
|
type: 'div',
|
|
35
35
|
displayName: 'View',
|
|
36
36
|
});
|
|
37
|
-
const SpanElement =
|
|
37
|
+
const SpanElement = defineBaseElementWithRef({
|
|
38
38
|
type: 'span',
|
|
39
39
|
displayName: 'Span',
|
|
40
40
|
});
|
|
41
|
-
const TextAreaElement =
|
|
41
|
+
const TextAreaElement = defineBaseElementWithRef({
|
|
42
42
|
type: 'textarea',
|
|
43
43
|
displayName: 'TextArea',
|
|
44
44
|
});
|
|
@@ -8,15 +8,11 @@ import { ViewElement } from './context/elements/definitions.mjs';
|
|
|
8
8
|
import { AIConversationProvider } from './AIConversationProvider.mjs';
|
|
9
9
|
import { DefaultMessageControl } from './views/Controls/DefaultMessageControl.mjs';
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* @experimental
|
|
13
|
-
*/
|
|
14
11
|
function createAIConversation(input = {}) {
|
|
15
|
-
const {
|
|
12
|
+
const { suggestedPrompts, actions, responseComponents, variant, controls, displayText, allowAttachments, messageRenderer, FallbackResponseComponent, } = input;
|
|
16
13
|
function AIConversation(props) {
|
|
17
14
|
const { messages, avatars, handleSendMessage, isLoading } = props;
|
|
18
15
|
const providerProps = {
|
|
19
|
-
elements,
|
|
20
16
|
actions,
|
|
21
17
|
suggestedPrompts,
|
|
22
18
|
responseComponents,
|
|
@@ -29,6 +25,7 @@ function createAIConversation(input = {}) {
|
|
|
29
25
|
handleSendMessage,
|
|
30
26
|
isLoading,
|
|
31
27
|
messageRenderer,
|
|
28
|
+
FallbackResponseComponent,
|
|
32
29
|
};
|
|
33
30
|
return (React__default.createElement(AIConversationProvider, { ...providerProps },
|
|
34
31
|
React__default.createElement(ViewElement, null,
|
|
@@ -2,6 +2,12 @@ import { formatDate } from './utils.mjs';
|
|
|
2
2
|
|
|
3
3
|
const defaultAIConversationDisplayTextEn = {
|
|
4
4
|
getMessageTimestampText: (date) => formatDate(date),
|
|
5
|
+
getMaxAttachmentErrorText(count) {
|
|
6
|
+
return `Cannot choose more than ${count} ${count === 1 ? 'file' : 'files'}. `;
|
|
7
|
+
},
|
|
8
|
+
getAttachmentSizeErrorText(sizeText) {
|
|
9
|
+
return `File size must be below ${sizeText}.`;
|
|
10
|
+
},
|
|
5
11
|
};
|
|
6
12
|
|
|
7
13
|
export { defaultAIConversationDisplayTextEn };
|
|
@@ -12,28 +12,57 @@ function formatDate(date) {
|
|
|
12
12
|
return `${dateString} at ${timeString}`;
|
|
13
13
|
}
|
|
14
14
|
function arrayBufferToBase64(buffer) {
|
|
15
|
-
let binary = '';
|
|
16
|
-
const bytes = new Uint8Array(buffer);
|
|
17
|
-
const len = bytes.byteLength;
|
|
18
|
-
for (let i = 0; i < len; i++) {
|
|
19
|
-
binary += String.fromCharCode(bytes[i]);
|
|
20
|
-
}
|
|
21
|
-
return window.btoa(binary);
|
|
22
|
-
}
|
|
23
|
-
function convertBufferToBase64(buffer, format) {
|
|
24
|
-
let base64string = '';
|
|
25
15
|
// Use node-based buffer if available
|
|
26
16
|
// fall back on browser if not
|
|
27
17
|
if (typeof Buffer !== 'undefined') {
|
|
28
|
-
|
|
18
|
+
return Buffer.from(new Uint8Array(buffer)).toString('base64');
|
|
29
19
|
}
|
|
30
20
|
else {
|
|
31
|
-
|
|
21
|
+
let binary = '';
|
|
22
|
+
const bytes = new Uint8Array(buffer);
|
|
23
|
+
const len = bytes.byteLength;
|
|
24
|
+
for (let i = 0; i < len; i++) {
|
|
25
|
+
binary += String.fromCharCode(bytes[i]);
|
|
26
|
+
}
|
|
27
|
+
return window.btoa(binary);
|
|
32
28
|
}
|
|
29
|
+
}
|
|
30
|
+
function convertBufferToBase64(buffer, format) {
|
|
31
|
+
const base64string = arrayBufferToBase64(buffer);
|
|
33
32
|
return `data:image/${format};base64,${base64string}`;
|
|
34
33
|
}
|
|
35
34
|
function getImageTypeFromMimeType(mimeType) {
|
|
36
35
|
return mimeType.split('/')[1];
|
|
37
36
|
}
|
|
37
|
+
async function attachmentsValidator({ files, maxAttachments, maxAttachmentSize, }) {
|
|
38
|
+
const acceptedFiles = [];
|
|
39
|
+
const rejectedFiles = [];
|
|
40
|
+
let hasMaxSizeError = false;
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
43
|
+
const base64 = arrayBufferToBase64(arrayBuffer);
|
|
44
|
+
if (base64.length < maxAttachmentSize) {
|
|
45
|
+
acceptedFiles.push(file);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
rejectedFiles.push(file);
|
|
49
|
+
hasMaxSizeError = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (acceptedFiles.length > maxAttachments) {
|
|
53
|
+
return {
|
|
54
|
+
acceptedFiles: acceptedFiles.slice(0, maxAttachments),
|
|
55
|
+
rejectedFiles: [...acceptedFiles.slice(maxAttachments), ...rejectedFiles],
|
|
56
|
+
hasMaxAttachmentsError: true,
|
|
57
|
+
hasMaxAttachmentSizeError: hasMaxSizeError,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
acceptedFiles,
|
|
62
|
+
rejectedFiles,
|
|
63
|
+
hasMaxAttachmentsError: false,
|
|
64
|
+
hasMaxAttachmentSizeError: hasMaxSizeError,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
38
67
|
|
|
39
|
-
export { convertBufferToBase64, formatDate, getImageTypeFromMimeType };
|
|
68
|
+
export { attachmentsValidator, convertBufferToBase64, formatDate, getImageTypeFromMimeType };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
|
|
3
|
+
import '../../context/AIContextContext.mjs';
|
|
3
4
|
import { ActionsContext } from '../../context/ActionsContext.mjs';
|
|
4
5
|
import '../../context/AvatarsContext.mjs';
|
|
5
6
|
import '../../context/ConversationInputContext.mjs';
|
|
@@ -14,6 +15,7 @@ import '../../context/SendMessageContext.mjs';
|
|
|
14
15
|
import '../../context/MessageRenderContext.mjs';
|
|
15
16
|
import '../../context/AttachmentContext.mjs';
|
|
16
17
|
import '../../context/WelcomeMessageContext.mjs';
|
|
18
|
+
import '../../context/FallbackComponentContext.mjs';
|
|
17
19
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
18
20
|
|
|
19
21
|
const { Button, Span, View } = AIConversationElements;
|
|
@@ -33,8 +35,7 @@ const Container = withBaseElementProps(View, {
|
|
|
33
35
|
});
|
|
34
36
|
const ActionsBarControl = ({ message, focusable, }) => {
|
|
35
37
|
const actions = React__default.useContext(ActionsContext);
|
|
36
|
-
return (React__default.createElement(Container, null, actions?.map((action, index) => (React__default.createElement(ActionButton, {
|
|
37
|
-
React__default.createElement(ActionIcon, { "data-testid": `action-icon-${action.displayName}` }, action.icon))))));
|
|
38
|
+
return (React__default.createElement(Container, null, actions?.map((action, index) => (React__default.createElement(ActionButton, { key: index, onClick: () => action.handler(message), tabIndex: focusable ? 0 : -1 }, action.component)))));
|
|
38
39
|
};
|
|
39
40
|
ActionsBarControl.Button = ActionButton;
|
|
40
41
|
ActionsBarControl.Container = Container;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
|
|
3
|
+
import '../../context/AIContextContext.mjs';
|
|
3
4
|
import '../../context/ActionsContext.mjs';
|
|
4
5
|
import '../../context/AvatarsContext.mjs';
|
|
5
6
|
import { ConversationInputContext } from '../../context/ConversationInputContext.mjs';
|
|
@@ -14,6 +15,7 @@ import '../../context/SendMessageContext.mjs';
|
|
|
14
15
|
import '../../context/MessageRenderContext.mjs';
|
|
15
16
|
import '../../context/AttachmentContext.mjs';
|
|
16
17
|
import '../../context/WelcomeMessageContext.mjs';
|
|
18
|
+
import '../../context/FallbackComponentContext.mjs';
|
|
17
19
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
18
20
|
import { useDropZone } from '@aws-amplify/ui-react-core';
|
|
19
21
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
|
|
3
|
+
import '../../context/AIContextContext.mjs';
|
|
3
4
|
import '../../context/ActionsContext.mjs';
|
|
4
5
|
import '../../context/AvatarsContext.mjs';
|
|
5
6
|
import { ConversationInputContext } from '../../context/ConversationInputContext.mjs';
|
|
@@ -14,6 +15,7 @@ import '../../context/SendMessageContext.mjs';
|
|
|
14
15
|
import '../../context/MessageRenderContext.mjs';
|
|
15
16
|
import '../../context/AttachmentContext.mjs';
|
|
16
17
|
import '../../context/WelcomeMessageContext.mjs';
|
|
18
|
+
import '../../context/FallbackComponentContext.mjs';
|
|
17
19
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
18
20
|
|
|
19
21
|
const { Button, Icon, ListItem, UnorderedList: ListElement, Span, Text, View, } = AIConversationElements;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
|
|
3
|
+
import '../../context/AIContextContext.mjs';
|
|
3
4
|
import '../../context/ActionsContext.mjs';
|
|
4
5
|
import { AvatarsContext } from '../../context/AvatarsContext.mjs';
|
|
5
6
|
import '../../context/ConversationInputContext.mjs';
|
|
@@ -14,6 +15,7 @@ import '../../context/SendMessageContext.mjs';
|
|
|
14
15
|
import '../../context/MessageRenderContext.mjs';
|
|
15
16
|
import '../../context/AttachmentContext.mjs';
|
|
16
17
|
import '../../context/WelcomeMessageContext.mjs';
|
|
18
|
+
import '../../context/FallbackComponentContext.mjs';
|
|
17
19
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
18
20
|
|
|
19
21
|
const { Icon, Span, Text, View } = AIConversationElements;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import '../../context/AIContextContext.mjs';
|
|
2
3
|
import '../../context/ActionsContext.mjs';
|
|
3
4
|
import '../../context/AvatarsContext.mjs';
|
|
4
5
|
import '../../context/ConversationInputContext.mjs';
|
|
@@ -13,6 +14,7 @@ import '../../context/SendMessageContext.mjs';
|
|
|
13
14
|
import '../../context/MessageRenderContext.mjs';
|
|
14
15
|
import '../../context/AttachmentContext.mjs';
|
|
15
16
|
import { WelcomeMessageContext } from '../../context/WelcomeMessageContext.mjs';
|
|
17
|
+
import '../../context/FallbackComponentContext.mjs';
|
|
16
18
|
import '../../context/elements/definitions.mjs';
|
|
17
19
|
import { PromptControl } from './PromptControl.mjs';
|
|
18
20
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
|
|
3
|
+
import { AIContextContext } from '../../context/AIContextContext.mjs';
|
|
3
4
|
import '../../context/ActionsContext.mjs';
|
|
4
5
|
import '../../context/AvatarsContext.mjs';
|
|
5
6
|
import { ConversationInputContext } from '../../context/ConversationInputContext.mjs';
|
|
6
7
|
import { MessagesContext } from '../../context/MessagesContext.mjs';
|
|
7
8
|
import '../../context/SuggestedPromptsContext.mjs';
|
|
8
9
|
import '../../context/MessageVariantContext.mjs';
|
|
9
|
-
import '../../context/DisplayTextContext.mjs';
|
|
10
|
+
import { useConversationDisplayText } from '../../context/DisplayTextContext.mjs';
|
|
10
11
|
import { ControlsContext } from '../../context/ControlsContext.mjs';
|
|
11
12
|
import { LoadingContext } from '../../context/LoadingContext.mjs';
|
|
12
13
|
import { ResponseComponentsContext, convertResponseComponentsToToolConfiguration } from '../../context/ResponseComponentsContext.mjs';
|
|
@@ -14,10 +15,12 @@ import { SendMessageContext } from '../../context/SendMessageContext.mjs';
|
|
|
14
15
|
import '../../context/MessageRenderContext.mjs';
|
|
15
16
|
import { AttachmentContext } from '../../context/AttachmentContext.mjs';
|
|
16
17
|
import '../../context/WelcomeMessageContext.mjs';
|
|
18
|
+
import '../../context/FallbackComponentContext.mjs';
|
|
17
19
|
import { AIConversationElements } from '../../context/elements/definitions.mjs';
|
|
18
20
|
import { AttachFileControl } from './AttachFileControl.mjs';
|
|
19
21
|
import { AttachmentListControl } from './AttachmentListControl.mjs';
|
|
20
|
-
import { getImageTypeFromMimeType } from '../../utils.mjs';
|
|
22
|
+
import { attachmentsValidator, getImageTypeFromMimeType } from '../../utils.mjs';
|
|
23
|
+
import { humanFileSize, isFunction } from '@aws-amplify/ui';
|
|
21
24
|
|
|
22
25
|
const { Button, Icon, Label: LabelElement, TextArea, View, } = AIConversationElements;
|
|
23
26
|
const FIELD_BLOCK = 'ai-field';
|
|
@@ -100,12 +103,16 @@ const InputContainer = withBaseElementProps(View, {
|
|
|
100
103
|
className: `${FIELD_BLOCK}__input-container`,
|
|
101
104
|
});
|
|
102
105
|
const FormControl = () => {
|
|
103
|
-
const { input, setInput } = React__default.useContext(ConversationInputContext);
|
|
106
|
+
const { input, setInput, error, setError } = React__default.useContext(ConversationInputContext);
|
|
104
107
|
const handleSendMessage = React__default.useContext(SendMessageContext);
|
|
105
|
-
const allowAttachments = React__default.useContext(AttachmentContext);
|
|
106
|
-
const
|
|
108
|
+
const { allowAttachments, maxAttachmentSize, maxAttachments } = React__default.useContext(AttachmentContext);
|
|
109
|
+
const displayText = useConversationDisplayText();
|
|
107
110
|
const responseComponents = React__default.useContext(ResponseComponentsContext);
|
|
111
|
+
const isLoading = React__default.useContext(LoadingContext);
|
|
112
|
+
const aiContext = React__default.useContext(AIContextContext);
|
|
113
|
+
const ref = React__default.useRef(null);
|
|
108
114
|
const controls = React__default.useContext(ControlsContext);
|
|
115
|
+
const [composing, setComposing] = React__default.useState(false);
|
|
109
116
|
const submitMessage = async () => {
|
|
110
117
|
ref.current?.reset();
|
|
111
118
|
const submittedContent = [];
|
|
@@ -130,6 +137,7 @@ const FormControl = () => {
|
|
|
130
137
|
if (handleSendMessage) {
|
|
131
138
|
handleSendMessage({
|
|
132
139
|
content: submittedContent,
|
|
140
|
+
aiContext: isFunction(aiContext) ? aiContext() : undefined,
|
|
133
141
|
toolConfiguration: convertResponseComponentsToToolConfiguration(responseComponents),
|
|
134
142
|
});
|
|
135
143
|
}
|
|
@@ -142,7 +150,7 @@ const FormControl = () => {
|
|
|
142
150
|
};
|
|
143
151
|
const handleOnKeyDown = (event) => {
|
|
144
152
|
const { key, shiftKey } = event;
|
|
145
|
-
if (key === 'Enter' && !shiftKey) {
|
|
153
|
+
if (key === 'Enter' && !shiftKey && !composing) {
|
|
146
154
|
event.preventDefault();
|
|
147
155
|
const hasInput = !!input?.text || (input?.files?.length && input?.files?.length > 0);
|
|
148
156
|
if (hasInput) {
|
|
@@ -150,15 +158,43 @@ const FormControl = () => {
|
|
|
150
158
|
}
|
|
151
159
|
}
|
|
152
160
|
};
|
|
161
|
+
const onValidate = React__default.useCallback(async (files) => {
|
|
162
|
+
const previousFiles = input?.files ?? [];
|
|
163
|
+
const { acceptedFiles, hasMaxAttachmentsError, hasMaxAttachmentSizeError, } = await attachmentsValidator({
|
|
164
|
+
files: [...files, ...previousFiles],
|
|
165
|
+
maxAttachments,
|
|
166
|
+
maxAttachmentSize,
|
|
167
|
+
});
|
|
168
|
+
if (hasMaxAttachmentsError || hasMaxAttachmentSizeError) {
|
|
169
|
+
const errors = [];
|
|
170
|
+
if (hasMaxAttachmentsError) {
|
|
171
|
+
errors.push(displayText.getMaxAttachmentErrorText(maxAttachments));
|
|
172
|
+
}
|
|
173
|
+
if (hasMaxAttachmentSizeError) {
|
|
174
|
+
errors.push(displayText.getAttachmentSizeErrorText(
|
|
175
|
+
// base64 size is about 137% that of the file size
|
|
176
|
+
// https://en.wikipedia.org/wiki/Base64#MIME
|
|
177
|
+
humanFileSize((maxAttachmentSize - 814) / 1.37, true)));
|
|
178
|
+
}
|
|
179
|
+
setError?.(errors.join(' '));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
setError?.(undefined);
|
|
183
|
+
}
|
|
184
|
+
setInput?.((prevValue) => ({
|
|
185
|
+
...prevValue,
|
|
186
|
+
files: acceptedFiles,
|
|
187
|
+
}));
|
|
188
|
+
}, [setInput, input, displayText, maxAttachmentSize, maxAttachments, setError]);
|
|
153
189
|
if (controls?.Form) {
|
|
154
|
-
return (React__default.createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, allowAttachments: allowAttachments }));
|
|
190
|
+
return (React__default.createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, onValidate: onValidate, allowAttachments: allowAttachments, isLoading: isLoading, error: error, setError: setError }));
|
|
155
191
|
}
|
|
156
192
|
return (React__default.createElement("form", { className: `${FIELD_BLOCK}__form`, onSubmit: handleSubmit, method: "post", ref: ref },
|
|
157
193
|
allowAttachments ? React__default.createElement(AttachFileControl, null) : null,
|
|
158
194
|
React__default.createElement(InputContainer, null,
|
|
159
195
|
React__default.createElement(VisuallyHidden, null,
|
|
160
196
|
React__default.createElement(Label, null)),
|
|
161
|
-
React__default.createElement(TextInput, { onKeyDown: handleOnKeyDown }),
|
|
197
|
+
React__default.createElement(TextInput, { onKeyDown: handleOnKeyDown, onCompositionStart: () => setComposing(true), onCompositionEnd: () => setComposing(false) }),
|
|
162
198
|
React__default.createElement(AttachmentListControl, null)),
|
|
163
199
|
React__default.createElement(SendButton, null,
|
|
164
200
|
React__default.createElement(SendIcon, null))));
|