@aws-amplify/ui-react-ai 0.3.2 → 1.0.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 +21 -33
- package/dist/esm/components/AIConversation/AIConversationProvider.mjs +22 -16
- package/dist/esm/components/AIConversation/context/AIContextContext.mjs +8 -0
- package/dist/esm/components/AIConversation/context/FallbackComponentContext.mjs +8 -0
- package/dist/esm/components/AIConversation/context/MessageRenderContext.mjs +9 -0
- package/dist/esm/components/AIConversation/context/ResponseComponentsContext.mjs +6 -2
- package/dist/esm/components/AIConversation/context/WelcomeMessageContext.mjs +8 -0
- package/dist/esm/components/AIConversation/createAIConversation.mjs +18 -22
- package/dist/esm/components/AIConversation/views/Controls/ActionsBarControl.mjs +6 -2
- 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/DefaultMessageControl.mjs +31 -0
- package/dist/esm/components/AIConversation/views/Controls/{FieldControl.mjs → FormControl.mjs} +22 -13
- package/dist/esm/components/AIConversation/views/Controls/MessagesControl.mjs +42 -42
- package/dist/esm/components/AIConversation/views/Controls/PromptControl.mjs +9 -36
- package/dist/esm/components/AIConversation/views/default/Form.mjs +4 -5
- package/dist/esm/components/AIConversation/views/default/MessageList.mjs +34 -16
- package/dist/esm/components/AIConversation/views/default/PromptList.mjs +1 -1
- 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 +3 -0
- package/dist/index.js +569 -444
- package/dist/types/components/AIConversation/AIConversation.d.ts +2 -19
- package/dist/types/components/AIConversation/AIConversationProvider.d.ts +2 -3
- package/dist/types/components/AIConversation/context/AIContextContext.d.ts +6 -0
- package/dist/types/components/AIConversation/context/ControlsContext.d.ts +1 -0
- package/dist/types/components/AIConversation/context/FallbackComponentContext.d.ts +7 -0
- package/dist/types/components/AIConversation/context/MessageRenderContext.d.ts +5 -0
- package/dist/types/components/AIConversation/context/ResponseComponentsContext.d.ts +2 -2
- package/dist/types/components/AIConversation/context/WelcomeMessageContext.d.ts +8 -0
- package/dist/types/components/AIConversation/context/elements/definitions.d.ts +1 -1
- package/dist/types/components/AIConversation/context/index.d.ts +5 -0
- package/dist/types/components/AIConversation/createAIConversation.d.ts +0 -3
- package/dist/types/components/AIConversation/index.d.ts +2 -1
- package/dist/types/components/AIConversation/types.d.ts +24 -36
- package/dist/types/components/AIConversation/utils.d.ts +2 -2
- package/dist/types/components/AIConversation/views/Controls/DefaultMessageControl.d.ts +2 -0
- package/dist/types/components/AIConversation/views/Controls/{FieldControl.d.ts → FormControl.d.ts} +2 -2
- package/dist/types/components/AIConversation/views/Controls/MessagesControl.d.ts +3 -9
- package/dist/types/components/AIConversation/views/Controls/PromptControl.d.ts +0 -3
- package/dist/types/components/AIConversation/views/Controls/index.d.ts +3 -4
- 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/components/AIConversation/views/index.d.ts +2 -3
- 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 +38 -7
- package/dist/types/version.d.ts +1 -0
- package/package.json +20 -6
- package/dist/ai-conversation-styles.css +0 -195
- package/dist/ai-conversation-styles.js +0 -2
- package/dist/esm/components/AIConversation/views/Controls/HeaderControl.mjs +0 -34
- package/dist/esm/components/AIConversation/views/ConversationView.mjs +0 -20
- package/dist/esm/hooks/AIContextProvider.mjs +0 -20
- package/dist/types/ai-conversation-styles.d.ts +0 -1
- package/dist/types/components/AIConversation/views/Controls/HeaderControl.d.ts +0 -9
- package/dist/types/components/AIConversation/views/ConversationView.d.ts +0 -2
- package/dist/types/hooks/AIContextProvider.d.ts +0 -17
package/dist/index.js
CHANGED
|
@@ -29,92 +29,9 @@ function _interopNamespace(e) {
|
|
|
29
29
|
|
|
30
30
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
image: 'M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm40-80h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z',
|
|
36
|
-
'send-message': 'M120-160v-640l760 320-760 320Zm80-120 474-200-474-200v140l240 60-240 60v140Zm0 0v-400 400Z',
|
|
37
|
-
'user-avatar': 'M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z',
|
|
38
|
-
};
|
|
39
|
-
const DEFAULT_ICON_ATTRIBUTES = {
|
|
40
|
-
'aria-hidden': true,
|
|
41
|
-
width: '24',
|
|
42
|
-
height: '24',
|
|
43
|
-
// `viewBox` coordinates map to `path` data in DEFAULT_ICON_PATHS
|
|
44
|
-
viewBox: '0 -960 960 960',
|
|
45
|
-
fill: 'none',
|
|
46
|
-
xmlns: 'http://www.w3.org/2000/svg',
|
|
47
|
-
};
|
|
48
|
-
const BaseIconElement = elements.defineBaseElement({
|
|
49
|
-
type: 'svg',
|
|
50
|
-
displayName: 'Icon',
|
|
51
|
-
});
|
|
52
|
-
const getIconProps = ({ variant, ...props }) => {
|
|
53
|
-
const pathData = variant ? DEFAULT_ICON_PATHS[variant] : undefined;
|
|
54
|
-
const children = pathData ? (React__namespace["default"].createElement("path", { d: pathData, fill: "currentColor" })) : undefined;
|
|
55
|
-
return {
|
|
56
|
-
...DEFAULT_ICON_ATTRIBUTES,
|
|
57
|
-
...props,
|
|
58
|
-
children: props.children ?? children,
|
|
59
|
-
variant,
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
const IconElement = elements.withBaseElementProps(BaseIconElement, getIconProps);
|
|
63
|
-
|
|
64
|
-
const LabelElement$1 = elements.defineBaseElement({
|
|
65
|
-
type: 'label',
|
|
66
|
-
displayName: 'Label',
|
|
67
|
-
});
|
|
68
|
-
const TextElement = elements.defineBaseElement({
|
|
69
|
-
type: 'p',
|
|
70
|
-
displayName: 'Text',
|
|
71
|
-
});
|
|
72
|
-
const UnorderedListElement = elements.defineBaseElement({
|
|
73
|
-
type: 'ul',
|
|
74
|
-
displayName: 'UnorderedList',
|
|
75
|
-
});
|
|
76
|
-
const ListItemElement = elements.defineBaseElement({
|
|
77
|
-
type: 'li',
|
|
78
|
-
displayName: 'ListItem',
|
|
79
|
-
});
|
|
80
|
-
const HeadingElement = elements.defineBaseElement({
|
|
81
|
-
type: 'h2',
|
|
82
|
-
displayName: 'Title',
|
|
83
|
-
});
|
|
84
|
-
const ImageElement = elements.defineBaseElement({
|
|
85
|
-
type: 'img',
|
|
86
|
-
displayName: 'Image',
|
|
87
|
-
});
|
|
88
|
-
const InputElement = elements.defineBaseElement({
|
|
89
|
-
type: 'input',
|
|
90
|
-
displayName: 'Input',
|
|
91
|
-
});
|
|
92
|
-
const ButtonElement = elements.defineBaseElement({ type: 'button', displayName: 'Button' });
|
|
93
|
-
const ViewElement = elements.defineBaseElement({
|
|
94
|
-
type: 'div',
|
|
95
|
-
displayName: 'View',
|
|
96
|
-
});
|
|
97
|
-
const SpanElement = elements.defineBaseElement({
|
|
98
|
-
type: 'span',
|
|
99
|
-
displayName: 'Span',
|
|
100
|
-
});
|
|
101
|
-
const TextAreaElement = elements.defineBaseElement({
|
|
102
|
-
type: 'textarea',
|
|
103
|
-
displayName: 'TextArea',
|
|
104
|
-
});
|
|
105
|
-
const AIConversationElements = {
|
|
106
|
-
Button: ButtonElement,
|
|
107
|
-
Heading: HeadingElement,
|
|
108
|
-
Icon: IconElement,
|
|
109
|
-
Input: InputElement,
|
|
110
|
-
Image: ImageElement,
|
|
111
|
-
Label: LabelElement$1,
|
|
112
|
-
ListItem: ListItemElement,
|
|
113
|
-
Span: SpanElement,
|
|
114
|
-
Text: TextElement,
|
|
115
|
-
TextArea: TextAreaElement,
|
|
116
|
-
UnorderedList: UnorderedListElement,
|
|
117
|
-
View: ViewElement,
|
|
32
|
+
const AIContextContext = React__namespace["default"].createContext(undefined);
|
|
33
|
+
const AIContextProvider = ({ children, aiContext, }) => {
|
|
34
|
+
return (React__namespace["default"].createElement(AIContextContext.Provider, { value: aiContext }, children));
|
|
118
35
|
};
|
|
119
36
|
|
|
120
37
|
const ActionsContext = React__namespace["default"].createContext(undefined);
|
|
@@ -229,8 +146,12 @@ const convertResponseComponentsToToolConfiguration = (responseComponents) => {
|
|
|
229
146
|
const { props } = responseComponents[toolName];
|
|
230
147
|
const requiredProps = [];
|
|
231
148
|
Object.keys(props).forEach((propName) => {
|
|
232
|
-
if (props[propName].required)
|
|
149
|
+
if (props[propName].required) {
|
|
233
150
|
requiredProps.push(propName);
|
|
151
|
+
// The inputSchema for a tool needs to not
|
|
152
|
+
// have `required` in the properties
|
|
153
|
+
props[propName].required = undefined;
|
|
154
|
+
}
|
|
234
155
|
});
|
|
235
156
|
tools[toolName] = {
|
|
236
157
|
description: responseComponents[toolName].description,
|
|
@@ -253,46 +174,154 @@ const SendMessageContextProvider = ({ children, handleSendMessage, }) => {
|
|
|
253
174
|
return (React__namespace["default"].createElement(SendMessageContext.Provider, { value: handleSendMessage }, children));
|
|
254
175
|
};
|
|
255
176
|
|
|
256
|
-
const {
|
|
177
|
+
const { MessageRendererContext, MessageRendererProvider, useMessageRenderer, } = uiReactCore.createContextUtilities({
|
|
178
|
+
contextName: 'MessageRenderer',
|
|
179
|
+
defaultValue: undefined,
|
|
180
|
+
errorMessage: '`useMessageRenderer` must be used with an AIConversation component',
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const AttachmentContext = React__namespace.createContext(false);
|
|
184
|
+
const AttachmentProvider = ({ children, allowAttachments, }) => {
|
|
185
|
+
return (React__namespace.createElement(AttachmentContext.Provider, { value: allowAttachments ?? false }, children));
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const WelcomeMessageContext = React__namespace.createContext(undefined);
|
|
189
|
+
const WelcomeMessageProvider = ({ children, welcomeMessage, }) => {
|
|
190
|
+
return (React__namespace.createElement(WelcomeMessageContext.Provider, { value: welcomeMessage }, children));
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const FallbackComponentContext = React__namespace["default"].createContext(undefined);
|
|
194
|
+
const FallbackComponentProvider = ({ children, FallbackComponent, }) => {
|
|
195
|
+
return (React__namespace["default"].createElement(FallbackComponentContext.Provider, { value: FallbackComponent }, children));
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const DEFAULT_ICON_PATHS = {
|
|
199
|
+
attach: 'M720-330q0 104-73 177T470-80q-104 0-177-73t-73-177v-370q0-75 52.5-127.5T400-880q75 0 127.5 52.5T580-700v350q0 46-32 78t-78 32q-46 0-78-32t-32-78v-370h80v370q0 13 8.5 21.5T470-320q13 0 21.5-8.5T500-350v-350q-1-42-29.5-71T400-800q-42 0-71 29t-29 71v370q-1 71 49 120.5T470-160q70 0 119-49.5T640-330v-390h80v390Z',
|
|
200
|
+
close: 'm256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z',
|
|
201
|
+
image: 'M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm40-80h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z',
|
|
202
|
+
'send-message': 'M120-160v-640l760 320-760 320Zm80-120 474-200-474-200v140l240 60-240 60v140Zm0 0v-400 400Z',
|
|
203
|
+
'user-avatar': 'M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z',
|
|
204
|
+
};
|
|
205
|
+
const DEFAULT_ICON_ATTRIBUTES = {
|
|
206
|
+
'aria-hidden': true,
|
|
207
|
+
width: '24',
|
|
208
|
+
height: '24',
|
|
209
|
+
// `viewBox` coordinates map to `path` data in DEFAULT_ICON_PATHS
|
|
210
|
+
viewBox: '0 -960 960 960',
|
|
211
|
+
fill: 'none',
|
|
212
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
213
|
+
};
|
|
214
|
+
const BaseIconElement = elements.defineBaseElement({
|
|
215
|
+
type: 'svg',
|
|
216
|
+
displayName: 'Icon',
|
|
217
|
+
});
|
|
218
|
+
const getIconProps = ({ variant, ...props }) => {
|
|
219
|
+
const pathData = variant ? DEFAULT_ICON_PATHS[variant] : undefined;
|
|
220
|
+
const children = pathData ? (React__namespace["default"].createElement("path", { d: pathData, fill: "currentColor" })) : undefined;
|
|
221
|
+
return {
|
|
222
|
+
...DEFAULT_ICON_ATTRIBUTES,
|
|
223
|
+
...props,
|
|
224
|
+
children: props.children ?? children,
|
|
225
|
+
variant,
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
const IconElement = elements.withBaseElementProps(BaseIconElement, getIconProps);
|
|
229
|
+
|
|
230
|
+
const LabelElement$1 = elements.defineBaseElement({
|
|
231
|
+
type: 'label',
|
|
232
|
+
displayName: 'Label',
|
|
233
|
+
});
|
|
234
|
+
const TextElement = elements.defineBaseElement({
|
|
235
|
+
type: 'p',
|
|
236
|
+
displayName: 'Text',
|
|
237
|
+
});
|
|
238
|
+
const UnorderedListElement = elements.defineBaseElement({
|
|
239
|
+
type: 'ul',
|
|
240
|
+
displayName: 'UnorderedList',
|
|
241
|
+
});
|
|
242
|
+
const ListItemElement = elements.defineBaseElement({
|
|
243
|
+
type: 'li',
|
|
244
|
+
displayName: 'ListItem',
|
|
245
|
+
});
|
|
246
|
+
const HeadingElement = elements.defineBaseElement({
|
|
247
|
+
type: 'h2',
|
|
248
|
+
displayName: 'Title',
|
|
249
|
+
});
|
|
250
|
+
const ImageElement = elements.defineBaseElement({
|
|
251
|
+
type: 'img',
|
|
252
|
+
displayName: 'Image',
|
|
253
|
+
});
|
|
254
|
+
const InputElement = elements.defineBaseElement({
|
|
255
|
+
type: 'input',
|
|
256
|
+
displayName: 'Input',
|
|
257
|
+
});
|
|
258
|
+
const ButtonElement = elements.defineBaseElement({ type: 'button', displayName: 'Button' });
|
|
259
|
+
const ViewElement = elements.defineBaseElement({
|
|
260
|
+
type: 'div',
|
|
261
|
+
displayName: 'View',
|
|
262
|
+
});
|
|
263
|
+
const SpanElement = elements.defineBaseElement({
|
|
264
|
+
type: 'span',
|
|
265
|
+
displayName: 'Span',
|
|
266
|
+
});
|
|
267
|
+
const TextAreaElement = elements.defineBaseElement({
|
|
268
|
+
type: 'textarea',
|
|
269
|
+
displayName: 'TextArea',
|
|
270
|
+
});
|
|
271
|
+
const AIConversationElements = {
|
|
272
|
+
Button: ButtonElement,
|
|
273
|
+
Heading: HeadingElement,
|
|
274
|
+
Icon: IconElement,
|
|
275
|
+
Input: InputElement,
|
|
276
|
+
Image: ImageElement,
|
|
277
|
+
Label: LabelElement$1,
|
|
278
|
+
ListItem: ListItemElement,
|
|
279
|
+
Span: SpanElement,
|
|
280
|
+
Text: TextElement,
|
|
281
|
+
TextArea: TextAreaElement,
|
|
282
|
+
UnorderedList: UnorderedListElement,
|
|
283
|
+
View: ViewElement,
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const { Button: Button$4, Span: Span$2, View: View$6 } = AIConversationElements;
|
|
257
287
|
const ACTIONS_BAR_BLOCK = 'ai-actions-bar';
|
|
258
|
-
const ActionIcon = elements.withBaseElementProps(Span$
|
|
288
|
+
const ActionIcon = elements.withBaseElementProps(Span$2, {
|
|
259
289
|
'aria-hidden': 'true',
|
|
260
290
|
className: `${ACTIONS_BAR_BLOCK}__icon`,
|
|
261
291
|
});
|
|
262
|
-
const ActionButtonBase = elements.withBaseElementProps(Button$
|
|
292
|
+
const ActionButtonBase = elements.withBaseElementProps(Button$4, {
|
|
263
293
|
className: `${ACTIONS_BAR_BLOCK}__button`,
|
|
264
294
|
});
|
|
265
295
|
const ActionButton = React__namespace["default"].forwardRef(function ActionButton(props, ref) {
|
|
266
296
|
return React__namespace["default"].createElement(ActionButtonBase, { ...props, ref: ref });
|
|
267
297
|
});
|
|
268
|
-
const Container$
|
|
298
|
+
const Container$3 = elements.withBaseElementProps(View$6, {
|
|
269
299
|
className: `${ACTIONS_BAR_BLOCK}__container`,
|
|
270
300
|
});
|
|
271
301
|
const ActionsBarControl = ({ message, focusable, }) => {
|
|
272
302
|
const actions = React__namespace["default"].useContext(ActionsContext);
|
|
273
|
-
return (React__namespace["default"].createElement(Container$
|
|
274
|
-
React__namespace["default"].createElement(ActionIcon, { "data-testid": `action-icon-${action.displayName}` }, action.icon))))));
|
|
303
|
+
return (React__namespace["default"].createElement(Container$3, null, actions?.map((action, index) => (React__namespace["default"].createElement(ActionButton, { key: index, onClick: () => action.handler(message), tabIndex: focusable ? 0 : -1 }, action.component)))));
|
|
275
304
|
};
|
|
276
305
|
ActionsBarControl.Button = ActionButton;
|
|
277
|
-
ActionsBarControl.Container = Container$
|
|
306
|
+
ActionsBarControl.Container = Container$3;
|
|
278
307
|
ActionsBarControl.Icon = ActionIcon;
|
|
279
308
|
|
|
280
|
-
const { Icon: Icon$
|
|
309
|
+
const { Icon: Icon$3, Span: Span$1, Text: Text$2, View: View$5 } = AIConversationElements;
|
|
281
310
|
const AVATAR_BLOCK = 'ai-avatar';
|
|
282
|
-
const DEFAULT_USER_ICON = elements.withBaseElementProps(Icon$
|
|
311
|
+
const DEFAULT_USER_ICON = elements.withBaseElementProps(Icon$3, {
|
|
283
312
|
variant: 'user-avatar',
|
|
284
313
|
});
|
|
285
314
|
const DEFAULT_AI_ICON = () => (React__namespace["default"].createElement("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", xmlns: "http://www.w3.org/2000/svg" },
|
|
286
315
|
React__namespace["default"].createElement("g", { id: "raven-logo" },
|
|
287
316
|
React__namespace["default"].createElement("path", { id: "Subtract", fillRule: "evenodd", clipRule: "evenodd", d: "M16 1.29833C14.7624 0.583803 13.2376 0.583804 12 1.29833L4.00006 5.91711C2.76246 6.63165 2.00006 7.95216 2.00006 9.38122V18.6188C2.00006 20.0478 2.76246 21.3684 4.00006 22.0829L12 26.7017C13.2376 27.4162 14.7624 27.4162 16 26.7017L24 22.0829C25.2376 21.3684 26 20.0478 26 18.6188V9.38122C26 7.95215 25.2376 6.63164 24 5.91711L16 1.29833ZM14.9379 6.37317C14.6157 5.50255 13.3843 5.50255 13.0622 6.37317L11.4151 10.8243C11.3138 11.098 11.098 11.3138 10.8243 11.4151L6.37317 13.0621C5.50256 13.3843 5.50256 14.6157 6.37317 14.9378L10.8243 16.5849C11.098 16.6862 11.3138 16.902 11.4151 17.1757L13.0622 21.6268C13.3843 22.4974 14.6157 22.4974 14.9379 21.6268L16.5849 17.1757C16.6862 16.902 16.902 16.6862 17.1757 16.5849L21.6268 14.9378C22.4974 14.6157 22.4974 13.3843 21.6268 13.0621L17.1757 11.4151C16.902 11.3138 16.6862 11.098 16.5849 10.8243L14.9379 6.37317Z", fill: "#0D1A26" }))));
|
|
288
|
-
const AvatarDisplayName = elements.withBaseElementProps(Text$
|
|
317
|
+
const AvatarDisplayName = elements.withBaseElementProps(Text$2, {
|
|
289
318
|
className: `${AVATAR_BLOCK}__display-name`,
|
|
290
319
|
});
|
|
291
|
-
const AvatarIcon = elements.withBaseElementProps(Span$
|
|
320
|
+
const AvatarIcon = elements.withBaseElementProps(Span$1, {
|
|
292
321
|
'aria-hidden': true,
|
|
293
322
|
className: `${AVATAR_BLOCK}__icon`,
|
|
294
323
|
});
|
|
295
|
-
const Container$
|
|
324
|
+
const Container$2 = elements.withBaseElementProps(View$5, {
|
|
296
325
|
className: `${AVATAR_BLOCK}__container`,
|
|
297
326
|
});
|
|
298
327
|
const AvatarControl = () => {
|
|
@@ -301,47 +330,18 @@ const AvatarControl = () => {
|
|
|
301
330
|
const avatar = role === 'assistant' ? avatars?.ai : avatars?.user;
|
|
302
331
|
const defaultIcon = role === 'assistant' ? React__namespace["default"].createElement(DEFAULT_AI_ICON, null) : React__namespace["default"].createElement(DEFAULT_USER_ICON, null);
|
|
303
332
|
const defaultDisplayName = role === 'user' ? 'User' : 'Assistant';
|
|
304
|
-
return (React__namespace["default"].createElement(Container$
|
|
333
|
+
return (React__namespace["default"].createElement(Container$2, { "data-testid": 'avatar' },
|
|
305
334
|
React__namespace["default"].createElement(AvatarIcon, { "data-testid": `avatar-icon-${role}` }, avatar?.avatar ?? defaultIcon),
|
|
306
335
|
React__namespace["default"].createElement(AvatarDisplayName, null, avatar?.username ?? defaultDisplayName)));
|
|
307
336
|
};
|
|
308
|
-
AvatarControl.Container = Container$
|
|
337
|
+
AvatarControl.Container = Container$2;
|
|
309
338
|
AvatarControl.DisplayName = AvatarDisplayName;
|
|
310
339
|
AvatarControl.Icon = AvatarIcon;
|
|
311
340
|
|
|
312
|
-
const {
|
|
313
|
-
const HEADER_BLOCK = 'ai-header';
|
|
314
|
-
const HeaderTextBase = elements.withBaseElementProps(Text$3, {
|
|
315
|
-
className: `${HEADER_BLOCK}__text`,
|
|
316
|
-
});
|
|
317
|
-
const HeaderText$1 = React__namespace["default"].forwardRef(function HeaderText(props, ref) {
|
|
318
|
-
return React__namespace["default"].createElement(HeaderTextBase, { ...props, ref: ref });
|
|
319
|
-
});
|
|
320
|
-
const CloseIcon = elements.withBaseElementProps(Icon$4, {
|
|
321
|
-
className: `${HEADER_BLOCK}__icon`,
|
|
322
|
-
variant: 'close',
|
|
323
|
-
});
|
|
324
|
-
const CloseButtonBase = elements.withBaseElementProps(Button$4, {
|
|
325
|
-
className: `${HEADER_BLOCK}__button`,
|
|
326
|
-
});
|
|
327
|
-
const CloseButton = React__namespace["default"].forwardRef(function CloseButton(props, ref) {
|
|
328
|
-
return React__namespace["default"].createElement(CloseButtonBase, { ...props, ref: ref });
|
|
329
|
-
});
|
|
330
|
-
const Container$2 = elements.withBaseElementProps(View$5, {
|
|
331
|
-
className: `${HEADER_BLOCK}__container`,
|
|
332
|
-
});
|
|
333
|
-
const HeaderControl = () => (React__namespace["default"].createElement(Container$2, null,
|
|
334
|
-
React__namespace["default"].createElement(HeaderText$1, null, "Raven Chat"),
|
|
335
|
-
React__namespace["default"].createElement(CloseButton, null,
|
|
336
|
-
React__namespace["default"].createElement(CloseIcon, null))));
|
|
337
|
-
HeaderControl.Container = Container$2;
|
|
338
|
-
HeaderControl.Text = HeaderText$1;
|
|
339
|
-
HeaderControl.Button = CloseButton;
|
|
340
|
-
|
|
341
|
-
const { Button: Button$3, Icon: Icon$3, View: View$4 } = AIConversationElements;
|
|
341
|
+
const { Button: Button$3, Icon: Icon$2, View: View$4 } = AIConversationElements;
|
|
342
342
|
const ATTACH_FILE_BLOCK = 'ai-attach-file';
|
|
343
343
|
const FIELD_BLOCK$1 = 'ai-field';
|
|
344
|
-
const AttachFileIcon = elements.withBaseElementProps(Icon$
|
|
344
|
+
const AttachFileIcon = elements.withBaseElementProps(Icon$2, {
|
|
345
345
|
className: `${ATTACH_FILE_BLOCK}__icon`,
|
|
346
346
|
variant: 'attach',
|
|
347
347
|
});
|
|
@@ -398,12 +398,12 @@ AttachFileControl.Icon = AttachFileIcon;
|
|
|
398
398
|
AttachFileControl.Button = AttachFileButton;
|
|
399
399
|
AttachFileControl.Container = AttachFileContainer;
|
|
400
400
|
|
|
401
|
-
const { Button: Button$2, Icon: Icon$
|
|
401
|
+
const { Button: Button$2, Icon: Icon$1, ListItem, UnorderedList: ListElement, Span, Text: Text$1, View: View$3, } = AIConversationElements;
|
|
402
402
|
const IMAGE_LIST_BLOCK = 'ai-attachment-list';
|
|
403
403
|
const IMAGE_ITEM_BLOCK = 'ai-attachment';
|
|
404
404
|
const REMOVE_IMAGE_BLOCK = 'ai-remove-attachment';
|
|
405
405
|
const IMAGE_TEXT_BLOCK = 'ai-attachment-text';
|
|
406
|
-
const RemoveIcon = elements.withBaseElementProps(Icon$
|
|
406
|
+
const RemoveIcon = elements.withBaseElementProps(Icon$1, {
|
|
407
407
|
className: `${REMOVE_IMAGE_BLOCK}__icon`,
|
|
408
408
|
variant: 'close',
|
|
409
409
|
});
|
|
@@ -419,17 +419,17 @@ const RemoveButtonControl = ({ onRemove }) => {
|
|
|
419
419
|
};
|
|
420
420
|
RemoveButtonControl.Icon = RemoveIcon;
|
|
421
421
|
RemoveButtonControl.Button = RemoveButton;
|
|
422
|
-
const ImageIcon = elements.withBaseElementProps(Icon$
|
|
422
|
+
const ImageIcon = elements.withBaseElementProps(Icon$1, {
|
|
423
423
|
className: `${IMAGE_ITEM_BLOCK}__icon`,
|
|
424
424
|
variant: 'image',
|
|
425
425
|
});
|
|
426
|
-
const FileNameText = elements.withBaseElementProps(Text$
|
|
426
|
+
const FileNameText = elements.withBaseElementProps(Text$1, {
|
|
427
427
|
className: `${IMAGE_TEXT_BLOCK}__file-name`,
|
|
428
428
|
});
|
|
429
|
-
const FileSizeText = elements.withBaseElementProps(Text$
|
|
429
|
+
const FileSizeText = elements.withBaseElementProps(Text$1, {
|
|
430
430
|
className: `${IMAGE_TEXT_BLOCK}__file-size`,
|
|
431
431
|
});
|
|
432
|
-
const Separator
|
|
432
|
+
const Separator = elements.withBaseElementProps(Span, {
|
|
433
433
|
'aria-hidden': true,
|
|
434
434
|
className: `${IMAGE_TEXT_BLOCK}__separator`,
|
|
435
435
|
children: '|',
|
|
@@ -440,13 +440,13 @@ const TextContainer = elements.withBaseElementProps(View$3, {
|
|
|
440
440
|
const TextControl = ({ fileName, fileSize }) => {
|
|
441
441
|
return (React__namespace["default"].createElement(TextContainer, null,
|
|
442
442
|
React__namespace["default"].createElement(FileNameText, null, fileName),
|
|
443
|
-
React__namespace["default"].createElement(Separator
|
|
443
|
+
React__namespace["default"].createElement(Separator, null),
|
|
444
444
|
React__namespace["default"].createElement(FileSizeText, null, fileSize)));
|
|
445
445
|
};
|
|
446
446
|
TextControl.Container = TextContainer;
|
|
447
447
|
TextControl.FileName = FileNameText;
|
|
448
448
|
TextControl.FileSize = FileSizeText;
|
|
449
|
-
TextControl.Separator = Separator
|
|
449
|
+
TextControl.Separator = Separator;
|
|
450
450
|
const Container$1 = elements.withBaseElementProps(ListItem, {
|
|
451
451
|
className: `${IMAGE_ITEM_BLOCK}__list-item`,
|
|
452
452
|
});
|
|
@@ -480,14 +480,9 @@ const AttachmentListControl = () => {
|
|
|
480
480
|
AttachmentListControl.List = UnorderedList;
|
|
481
481
|
AttachmentListControl.Item = AttachmentControl;
|
|
482
482
|
|
|
483
|
-
const
|
|
484
|
-
const AttachmentProvider = ({ children, allowAttachments, }) => {
|
|
485
|
-
return (React__namespace.createElement(AttachmentContext.Provider, { value: allowAttachments ?? false }, children));
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
const { Button: Button$1, Icon: Icon$1, Label: LabelElement, TextArea, View: View$2, } = AIConversationElements;
|
|
483
|
+
const { Button: Button$1, Icon, Label: LabelElement, TextArea, View: View$2, } = AIConversationElements;
|
|
489
484
|
const FIELD_BLOCK = 'ai-field';
|
|
490
|
-
const SendIcon = elements.withBaseElementProps(Icon
|
|
485
|
+
const SendIcon = elements.withBaseElementProps(Icon, {
|
|
491
486
|
className: `${FIELD_BLOCK}__icon`,
|
|
492
487
|
variant: 'send-message',
|
|
493
488
|
});
|
|
@@ -565,13 +560,16 @@ const TextInput = React__namespace["default"].forwardRef(function TextInput(prop
|
|
|
565
560
|
const InputContainer = elements.withBaseElementProps(View$2, {
|
|
566
561
|
className: `${FIELD_BLOCK}__input-container`,
|
|
567
562
|
});
|
|
568
|
-
const
|
|
563
|
+
const FormControl = () => {
|
|
569
564
|
const { input, setInput } = React__namespace["default"].useContext(ConversationInputContext);
|
|
570
565
|
const handleSendMessage = React__namespace["default"].useContext(SendMessageContext);
|
|
571
566
|
const allowAttachments = React__namespace["default"].useContext(AttachmentContext);
|
|
572
|
-
const ref = React__namespace["default"].useRef(null);
|
|
573
567
|
const responseComponents = React__namespace["default"].useContext(ResponseComponentsContext);
|
|
568
|
+
const isLoading = React__namespace["default"].useContext(LoadingContext);
|
|
569
|
+
const aiContext = React__namespace["default"].useContext(AIContextContext);
|
|
570
|
+
const ref = React__namespace["default"].useRef(null);
|
|
574
571
|
const controls = React__namespace["default"].useContext(ControlsContext);
|
|
572
|
+
const [composing, setComposing] = React__namespace["default"].useState(false);
|
|
575
573
|
const submitMessage = async () => {
|
|
576
574
|
ref.current?.reset();
|
|
577
575
|
const submittedContent = [];
|
|
@@ -596,6 +594,7 @@ const FieldControl = () => {
|
|
|
596
594
|
if (handleSendMessage) {
|
|
597
595
|
handleSendMessage({
|
|
598
596
|
content: submittedContent,
|
|
597
|
+
aiContext: ui.isFunction(aiContext) ? aiContext() : undefined,
|
|
599
598
|
toolConfiguration: convertResponseComponentsToToolConfiguration(responseComponents),
|
|
600
599
|
});
|
|
601
600
|
}
|
|
@@ -608,7 +607,7 @@ const FieldControl = () => {
|
|
|
608
607
|
};
|
|
609
608
|
const handleOnKeyDown = (event) => {
|
|
610
609
|
const { key, shiftKey } = event;
|
|
611
|
-
if (key === 'Enter' && !shiftKey) {
|
|
610
|
+
if (key === 'Enter' && !shiftKey && !composing) {
|
|
612
611
|
event.preventDefault();
|
|
613
612
|
const hasInput = !!input?.text || (input?.files?.length && input?.files?.length > 0);
|
|
614
613
|
if (hasInput) {
|
|
@@ -617,77 +616,72 @@ const FieldControl = () => {
|
|
|
617
616
|
}
|
|
618
617
|
};
|
|
619
618
|
if (controls?.Form) {
|
|
620
|
-
return (React__namespace["default"].createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, allowAttachments: allowAttachments }));
|
|
619
|
+
return (React__namespace["default"].createElement(controls.Form, { handleSubmit: handleSubmit, input: input, setInput: setInput, allowAttachments: allowAttachments, isLoading: isLoading }));
|
|
621
620
|
}
|
|
622
621
|
return (React__namespace["default"].createElement("form", { className: `${FIELD_BLOCK}__form`, onSubmit: handleSubmit, method: "post", ref: ref },
|
|
623
622
|
allowAttachments ? React__namespace["default"].createElement(AttachFileControl, null) : null,
|
|
624
623
|
React__namespace["default"].createElement(InputContainer, null,
|
|
625
624
|
React__namespace["default"].createElement(VisuallyHidden, null,
|
|
626
625
|
React__namespace["default"].createElement(Label, null)),
|
|
627
|
-
React__namespace["default"].createElement(TextInput, { onKeyDown: handleOnKeyDown }),
|
|
626
|
+
React__namespace["default"].createElement(TextInput, { onKeyDown: handleOnKeyDown, onCompositionStart: () => setComposing(true), onCompositionEnd: () => setComposing(false) }),
|
|
628
627
|
React__namespace["default"].createElement(AttachmentListControl, null)),
|
|
629
628
|
React__namespace["default"].createElement(SendButton, null,
|
|
630
629
|
React__namespace["default"].createElement(SendIcon, null))));
|
|
631
630
|
};
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
631
|
+
FormControl.AttachFile = AttachFileControl;
|
|
632
|
+
FormControl.InputContainer = InputContainer;
|
|
633
|
+
FormControl.Label = Label;
|
|
634
|
+
FormControl.TextInput = TextInput;
|
|
635
|
+
FormControl.SendButton = SendButton;
|
|
636
|
+
FormControl.SendIcon = SendIcon;
|
|
638
637
|
|
|
639
|
-
const {
|
|
640
|
-
const MESSAGES_BLOCK = 'ai-
|
|
641
|
-
const MESSAGE_BLOCK = 'ai-
|
|
642
|
-
const
|
|
643
|
-
alt: 'Image attachment',
|
|
644
|
-
});
|
|
645
|
-
const MediaContent = React__namespace["default"].forwardRef(function MediaContent(props, ref) {
|
|
638
|
+
const { Text, View: View$1 } = AIConversationElements;
|
|
639
|
+
const MESSAGES_BLOCK = 'amplify-ai-conversation__message__list';
|
|
640
|
+
const MESSAGE_BLOCK = 'amplify-ai-conversation__message';
|
|
641
|
+
const MediaContent = (props) => {
|
|
646
642
|
const variant = React__namespace["default"].useContext(MessageVariantContext);
|
|
647
643
|
const role = React__namespace["default"].useContext(RoleContext);
|
|
648
|
-
return (React__namespace["default"].createElement(
|
|
649
|
-
}
|
|
644
|
+
return (React__namespace["default"].createElement(uiReact.Image, { className: ui.classNames(`${MESSAGE_BLOCK}__image`, variant && `${MESSAGE_BLOCK}__image--${variant}`, `${MESSAGE_BLOCK}__image--${role}`), ...props }));
|
|
645
|
+
};
|
|
650
646
|
const TextContent = React__namespace["default"].forwardRef(function TextContent(props, ref) {
|
|
651
|
-
return React__namespace["default"].createElement(Text
|
|
652
|
-
});
|
|
653
|
-
const ContentContainer = React__namespace["default"].forwardRef(function ContentContainer(props, ref) {
|
|
654
|
-
const variant = React__namespace["default"].useContext(MessageVariantContext);
|
|
655
|
-
return (React__namespace["default"].createElement(View$1, { "data-testid": 'content', className: `${MESSAGE_BLOCK}__content ${MESSAGE_BLOCK}__content--${variant}`, ref: ref, ...props }));
|
|
647
|
+
return React__namespace["default"].createElement(Text, { ref: ref, className: `${MESSAGE_BLOCK}__text`, ...props });
|
|
656
648
|
});
|
|
649
|
+
const ToolContent = ({ toolUse, }) => {
|
|
650
|
+
const responseComponents = React__namespace["default"].useContext(ResponseComponentsContext) ?? {};
|
|
651
|
+
const FallbackComponent = React__namespace["default"].useContext(FallbackComponentContext);
|
|
652
|
+
// For now tool use is limited to custom response components
|
|
653
|
+
const { name, input } = toolUse;
|
|
654
|
+
if (!name || !name.startsWith(RESPONSE_COMPONENT_PREFIX)) {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
const response = responseComponents[name];
|
|
659
|
+
if (response) {
|
|
660
|
+
const CustomComponent = response.component;
|
|
661
|
+
return React__namespace["default"].createElement(CustomComponent, { ...input });
|
|
662
|
+
}
|
|
663
|
+
// fallback if there is a UI component message but we don't have
|
|
664
|
+
// a React component that matches
|
|
665
|
+
if (FallbackComponent) {
|
|
666
|
+
return React__namespace["default"].createElement(FallbackComponent, { ...input });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
};
|
|
657
670
|
const MessageControl = ({ message }) => {
|
|
658
|
-
const
|
|
659
|
-
return (React__namespace["default"].createElement(
|
|
671
|
+
const messageRenderer = React__namespace["default"].useContext(MessageRendererContext);
|
|
672
|
+
return (React__namespace["default"].createElement(React__namespace["default"].Fragment, null, message.content.map((content, index) => {
|
|
660
673
|
if (content.text) {
|
|
661
|
-
return (React__namespace["default"].createElement(TextContent, { "data-testid": 'text-content', key: index }, content.text));
|
|
674
|
+
return messageRenderer?.text ? (React__namespace["default"].createElement(React__namespace["default"].Fragment, { key: index }, messageRenderer.text({ text: content.text }))) : (React__namespace["default"].createElement(TextContent, { "data-testid": 'text-content', key: index }, content.text));
|
|
662
675
|
}
|
|
663
676
|
else if (content.image) {
|
|
664
|
-
return (React__namespace["default"].createElement(MediaContent, { "data-testid": 'image-content', key: index, src: convertBufferToBase64(content.image?.source.bytes, content.image?.format) }));
|
|
677
|
+
return messageRenderer?.image ? (React__namespace["default"].createElement(React__namespace["default"].Fragment, { key: index }, messageRenderer?.image({ image: content.image }))) : (React__namespace["default"].createElement(MediaContent, { "data-testid": 'image-content', key: index, alt: "", src: convertBufferToBase64(content.image?.source.bytes, content.image?.format) }));
|
|
665
678
|
}
|
|
666
679
|
else if (content.toolUse) {
|
|
667
|
-
|
|
668
|
-
const { name, input } = content.toolUse;
|
|
669
|
-
if (!responseComponents ||
|
|
670
|
-
!name ||
|
|
671
|
-
!name.startsWith(RESPONSE_COMPONENT_PREFIX)) {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
else {
|
|
675
|
-
const response = responseComponents[name];
|
|
676
|
-
const CustomComponent = response.component;
|
|
677
|
-
return React__namespace["default"].createElement(CustomComponent, { ...input, key: index });
|
|
678
|
-
}
|
|
680
|
+
return React__namespace["default"].createElement(ToolContent, { toolUse: content.toolUse, key: index });
|
|
679
681
|
}
|
|
680
682
|
})));
|
|
681
683
|
};
|
|
682
|
-
|
|
683
|
-
MessageControl.MediaContent = MediaContent;
|
|
684
|
-
MessageControl.TextContent = TextContent;
|
|
685
|
-
const Separator = elements.withBaseElementProps(Span, {
|
|
686
|
-
'aria-hidden': true,
|
|
687
|
-
children: '|',
|
|
688
|
-
className: `${MESSAGE_BLOCK}__separator`,
|
|
689
|
-
});
|
|
690
|
-
const Timestamp = elements.withBaseElementProps(Text$1, {
|
|
684
|
+
const Timestamp = elements.withBaseElementProps(Text, {
|
|
691
685
|
className: `${MESSAGE_BLOCK}__timestamp`,
|
|
692
686
|
});
|
|
693
687
|
const HeaderContainer = React__namespace["default"].forwardRef(function HeaderContainer(props, ref) {
|
|
@@ -703,7 +697,7 @@ const Layout = React__namespace["default"].forwardRef(function Layout(props, ref
|
|
|
703
697
|
const variant = React__namespace["default"].useContext(MessageVariantContext);
|
|
704
698
|
return (React__namespace["default"].createElement(View$1, { ref: ref, className: `${MESSAGES_BLOCK}__container ${MESSAGES_BLOCK}__container--${variant}`, "aria-live": 'assertive', ...props }));
|
|
705
699
|
});
|
|
706
|
-
const MessagesControl = (
|
|
700
|
+
const MessagesControl = () => {
|
|
707
701
|
const messages = React__namespace["default"].useContext(MessagesContext);
|
|
708
702
|
const controls = React__namespace["default"].useContext(ControlsContext);
|
|
709
703
|
const { getMessageTimestampText } = useConversationDisplayText();
|
|
@@ -743,11 +737,10 @@ const MessagesControl = ({ renderMessage }) => {
|
|
|
743
737
|
content.text ??
|
|
744
738
|
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
745
739
|
return (React__namespace["default"].createElement(Layout, null, messagesWithRenderableContent?.map((message, index) => {
|
|
746
|
-
return
|
|
740
|
+
return (React__namespace["default"].createElement(RoleContext.Provider, { value: message.role, key: `message-${index}` },
|
|
747
741
|
React__namespace["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) },
|
|
748
742
|
React__namespace["default"].createElement(HeaderContainer, null,
|
|
749
743
|
React__namespace["default"].createElement(AvatarControl, null),
|
|
750
|
-
React__namespace["default"].createElement(Separator, null),
|
|
751
744
|
React__namespace["default"].createElement(Timestamp, null, getMessageTimestampText(new Date(message.createdAt)))),
|
|
752
745
|
React__namespace["default"].createElement(MessageControl, { message: message }),
|
|
753
746
|
message.role === 'assistant' ? (React__namespace["default"].createElement(ActionsBarControl, { message: message, focusable: focusedItemIndex === index })) : null)));
|
|
@@ -759,9 +752,8 @@ MessagesControl.Container = MessageContainer;
|
|
|
759
752
|
MessagesControl.HeaderContainer = HeaderContainer;
|
|
760
753
|
MessagesControl.Layout = Layout;
|
|
761
754
|
MessagesControl.Message = MessageControl;
|
|
762
|
-
MessagesControl.Separator = Separator;
|
|
763
755
|
|
|
764
|
-
const { View, Button
|
|
756
|
+
const { View, Button } = AIConversationElements;
|
|
765
757
|
const PROMPT_BLOCK = 'ai-prompts';
|
|
766
758
|
const PROMPT_CONTROL = `${PROMPT_BLOCK}__prompt`;
|
|
767
759
|
const PROMPT_CARD = `${PROMPT_CONTROL}__card`;
|
|
@@ -769,25 +761,6 @@ const PromptCard = elements.withBaseElementProps(Button, {
|
|
|
769
761
|
className: PROMPT_CARD,
|
|
770
762
|
type: 'button',
|
|
771
763
|
});
|
|
772
|
-
const AIIconProps = () => ({
|
|
773
|
-
children: (React__namespace["default"].createElement(React__namespace["default"].Fragment, null,
|
|
774
|
-
React__namespace["default"].createElement("path", { d: "M17.5 1.64858C19.047 0.755412 20.953 0.755412 22.5 1.64858L34.6428 8.65923C36.1898 9.55239 37.1428 11.203 37.1428 12.9894V27.0107C37.1428 28.797 36.1898 30.4476 34.6428 31.3408L22.5 38.3514C20.953 39.2446 19.047 39.2446 17.5 38.3514L5.35718 31.3408C3.81017 30.4476 2.85718 28.797 2.85718 27.0107V12.9894C2.85718 11.203 3.81017 9.55239 5.35718 8.65923L17.5 1.64858Z", fill: "white" }),
|
|
775
|
-
React__namespace["default"].createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M22.5 1.64851C20.953 0.755347 19.047 0.755347 17.5 1.64851L5.35718 8.65916C3.81017 9.55233 2.85718 11.203 2.85718 12.9893V27.0106C2.85718 28.7969 3.81017 30.4476 5.35718 31.3407L17.5 38.3514C19.047 39.2445 20.953 39.2445 22.5 38.3514L34.6428 31.3407C36.1898 30.4476 37.1428 28.7969 37.1428 27.0106V12.9893C37.1428 11.203 36.1898 9.55233 34.6428 8.65916L22.5 1.64851ZM20.9378 8.01826C20.6156 7.14764 19.3843 7.14764 19.0621 8.01825L16.2388 15.648C16.1375 15.9217 15.9217 16.1375 15.648 16.2388L8.01826 19.0621C7.14765 19.3842 7.14765 20.6156 8.01826 20.9378L15.648 23.7611C15.9217 23.8623 16.1375 24.0782 16.2388 24.3519L19.0621 31.9816C19.3843 32.8522 20.6156 32.8522 20.9378 31.9816L23.7611 24.3519C23.8624 24.0782 24.0782 23.8623 24.3519 23.7611L31.9816 20.9378C32.8523 20.6156 32.8523 19.3842 31.9816 19.0621L24.3519 16.2388C24.0782 16.1375 23.8624 15.9217 23.7611 15.648L20.9378 8.01826Z", fill: "url(#paint0_linear_395_1815)" }),
|
|
776
|
-
React__namespace["default"].createElement("defs", null,
|
|
777
|
-
React__namespace["default"].createElement("linearGradient", { id: "paint0_linear_395_1815", x1: "20", y1: "0.978638", x2: "20", y2: "39.0213", gradientUnits: "userSpaceOnUse" },
|
|
778
|
-
React__namespace["default"].createElement("stop", { stopColor: "#7DD6E8" }),
|
|
779
|
-
React__namespace["default"].createElement("stop", { offset: "1", stopColor: "#BF40BF" }))))),
|
|
780
|
-
className: `${PROMPT_CONTROL}__icon`,
|
|
781
|
-
width: '40',
|
|
782
|
-
height: '40',
|
|
783
|
-
viewBox: '0 0 40 40',
|
|
784
|
-
fill: 'none',
|
|
785
|
-
xmlns: 'http://www.w3.org/2000/svg',
|
|
786
|
-
});
|
|
787
|
-
const AIIcon = elements.withBaseElementProps(Icon, AIIconProps);
|
|
788
|
-
const HeaderText = elements.withBaseElementProps(Heading, {
|
|
789
|
-
className: `${PROMPT_CONTROL}__header`,
|
|
790
|
-
});
|
|
791
764
|
const PromptGroupBase = elements.withBaseElementProps(View, {
|
|
792
765
|
className: `${PROMPT_CONTROL}__buttongroup`,
|
|
793
766
|
});
|
|
@@ -802,9 +775,7 @@ const PromptGroup = React__namespace["default"].forwardRef(function ButtonGroup(
|
|
|
802
775
|
setInput((prevInput) => ({
|
|
803
776
|
...prevInput,
|
|
804
777
|
text: prompt.inputText,
|
|
805
|
-
})) },
|
|
806
|
-
React__namespace["default"].createElement(Text, { className: ui.classNames(`${PROMPT_CARD}__header`, `${PROMPT_CARD}__text`) }, prompt.header),
|
|
807
|
-
React__namespace["default"].createElement(Text, { className: `${PROMPT_CARD}__text` }, prompt.inputText)));
|
|
778
|
+
})) }, prompt.component));
|
|
808
779
|
})));
|
|
809
780
|
});
|
|
810
781
|
const Container = elements.withBaseElementProps(View, {
|
|
@@ -818,61 +789,50 @@ const PromptControl = () => {
|
|
|
818
789
|
return (React__namespace["default"].createElement(controls.PromptList, { setInput: setInput, suggestedPrompts: suggestedPromptsArray }));
|
|
819
790
|
}
|
|
820
791
|
return (React__namespace["default"].createElement(Container, null,
|
|
821
|
-
React__namespace["default"].createElement(AIIcon, null),
|
|
822
|
-
React__namespace["default"].createElement(HeaderText, null, "How can I help you today?"),
|
|
823
792
|
React__namespace["default"].createElement(PromptGroup, null)));
|
|
824
793
|
};
|
|
825
|
-
const AutoHidablePromptControl = () => {
|
|
826
|
-
const messages = React__namespace["default"].useContext(MessagesContext);
|
|
827
|
-
if (!messages || messages.length === 0) {
|
|
828
|
-
return React__namespace["default"].createElement(PromptControl, null);
|
|
829
|
-
}
|
|
830
|
-
};
|
|
831
794
|
PromptControl.Container = Container;
|
|
832
|
-
PromptControl.Header = HeaderText;
|
|
833
|
-
PromptControl.Icon = AIIcon;
|
|
834
795
|
PromptControl.PromptGroup = PromptGroup;
|
|
835
796
|
PromptControl.PromptCard = PromptCard;
|
|
836
797
|
|
|
837
|
-
|
|
838
|
-
return (React__namespace["default"].createElement(ViewElement, null,
|
|
839
|
-
React__namespace["default"].createElement(HeaderControl, null),
|
|
840
|
-
React__namespace["default"].createElement(ViewElement, null,
|
|
841
|
-
React__namespace["default"].createElement(AutoHidablePromptControl, null),
|
|
842
|
-
React__namespace["default"].createElement(MessagesControl, null)),
|
|
843
|
-
React__namespace["default"].createElement(ViewElement, null,
|
|
844
|
-
React__namespace["default"].createElement(FieldControl, null))));
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
const AIConversationProvider = ({ elements: elements$1, actions, suggestedPrompts, responseComponents, variant, controls, displayText, allowAttachments, messages, handleSendMessage, avatars, isLoading, children, }) => {
|
|
798
|
+
const AIConversationProvider = ({ aiContext, actions, allowAttachments, avatars, children, controls, displayText, handleSendMessage, isLoading, messages, messageRenderer, responseComponents, suggestedPrompts, variant, welcomeMessage, FallbackResponseComponent, }) => {
|
|
848
799
|
const _displayText = {
|
|
849
800
|
...defaultAIConversationDisplayTextEn,
|
|
850
801
|
...displayText,
|
|
851
802
|
};
|
|
852
|
-
return (React__namespace["default"].createElement(
|
|
853
|
-
React__namespace["default"].createElement(
|
|
854
|
-
React__namespace["default"].createElement(
|
|
855
|
-
React__namespace["default"].createElement(
|
|
856
|
-
React__namespace["default"].createElement(
|
|
857
|
-
React__namespace["default"].createElement(
|
|
858
|
-
React__namespace["default"].createElement(
|
|
859
|
-
React__namespace["default"].createElement(
|
|
860
|
-
React__namespace["default"].createElement(
|
|
861
|
-
React__namespace["default"].createElement(
|
|
862
|
-
React__namespace["default"].createElement(
|
|
863
|
-
React__namespace["default"].createElement(
|
|
864
|
-
React__namespace["default"].createElement(
|
|
803
|
+
return (React__namespace["default"].createElement(ControlsProvider, { controls: controls },
|
|
804
|
+
React__namespace["default"].createElement(SuggestedPromptProvider, { suggestedPrompts: suggestedPrompts },
|
|
805
|
+
React__namespace["default"].createElement(WelcomeMessageProvider, { welcomeMessage: welcomeMessage },
|
|
806
|
+
React__namespace["default"].createElement(FallbackComponentProvider, { FallbackComponent: FallbackResponseComponent },
|
|
807
|
+
React__namespace["default"].createElement(MessageRendererProvider, { ...messageRenderer },
|
|
808
|
+
React__namespace["default"].createElement(ResponseComponentsProvider, { responseComponents: responseComponents },
|
|
809
|
+
React__namespace["default"].createElement(AttachmentProvider, { allowAttachments: allowAttachments },
|
|
810
|
+
React__namespace["default"].createElement(ConversationDisplayTextProvider, { ..._displayText },
|
|
811
|
+
React__namespace["default"].createElement(ConversationInputContextProvider, null,
|
|
812
|
+
React__namespace["default"].createElement(SendMessageContextProvider, { handleSendMessage: handleSendMessage },
|
|
813
|
+
React__namespace["default"].createElement(AvatarsProvider, { avatars: avatars },
|
|
814
|
+
React__namespace["default"].createElement(ActionsProvider, { actions: actions },
|
|
815
|
+
React__namespace["default"].createElement(MessageVariantProvider, { variant: variant },
|
|
816
|
+
React__namespace["default"].createElement(MessagesProvider, { messages: messages },
|
|
817
|
+
React__namespace["default"].createElement(AIContextProvider, { aiContext: aiContext },
|
|
818
|
+
React__namespace["default"].createElement(LoadingContextProvider, { isLoading: isLoading }, children)))))))))))))))));
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
const DefaultMessageControl = () => {
|
|
822
|
+
const messages = React__namespace.useContext(MessagesContext);
|
|
823
|
+
const welcomeMessage = React__namespace.useContext(WelcomeMessageContext);
|
|
824
|
+
if (!messages || messages.length === 0) {
|
|
825
|
+
return (React__namespace.createElement(React__namespace.Fragment, null,
|
|
826
|
+
welcomeMessage,
|
|
827
|
+
React__namespace.createElement(PromptControl, null)));
|
|
828
|
+
}
|
|
865
829
|
};
|
|
866
830
|
|
|
867
|
-
/**
|
|
868
|
-
* @experimental
|
|
869
|
-
*/
|
|
870
831
|
function createAIConversation(input = {}) {
|
|
871
|
-
const {
|
|
832
|
+
const { suggestedPrompts, actions, responseComponents, variant, controls, displayText, allowAttachments, messageRenderer, FallbackResponseComponent, } = input;
|
|
872
833
|
function AIConversation(props) {
|
|
873
834
|
const { messages, avatars, handleSendMessage, isLoading } = props;
|
|
874
835
|
const providerProps = {
|
|
875
|
-
elements,
|
|
876
836
|
actions,
|
|
877
837
|
suggestedPrompts,
|
|
878
838
|
responseComponents,
|
|
@@ -884,24 +844,34 @@ function createAIConversation(input = {}) {
|
|
|
884
844
|
avatars,
|
|
885
845
|
handleSendMessage,
|
|
886
846
|
isLoading,
|
|
847
|
+
messageRenderer,
|
|
848
|
+
FallbackResponseComponent,
|
|
887
849
|
};
|
|
888
850
|
return (React__namespace["default"].createElement(AIConversationProvider, { ...providerProps },
|
|
889
|
-
React__namespace["default"].createElement(
|
|
851
|
+
React__namespace["default"].createElement(ViewElement, null,
|
|
852
|
+
React__namespace["default"].createElement(ViewElement, null,
|
|
853
|
+
React__namespace["default"].createElement(DefaultMessageControl, null),
|
|
854
|
+
React__namespace["default"].createElement(MessagesControl, null)),
|
|
855
|
+
React__namespace["default"].createElement(ViewElement, null,
|
|
856
|
+
React__namespace["default"].createElement(FormControl, null)))));
|
|
890
857
|
}
|
|
891
|
-
const Controls = {
|
|
892
|
-
ActionsBar: ActionsBarControl,
|
|
893
|
-
Avatars: AvatarControl,
|
|
894
|
-
Field: FieldControl,
|
|
895
|
-
Header: HeaderControl,
|
|
896
|
-
Messages: MessagesControl,
|
|
897
|
-
SuggestedPrompts: PromptControl,
|
|
898
|
-
};
|
|
899
858
|
AIConversation.Provider = AIConversationProvider;
|
|
900
|
-
AIConversation.
|
|
901
|
-
AIConversation.
|
|
859
|
+
AIConversation.DefaultMessage = DefaultMessageControl;
|
|
860
|
+
AIConversation.Messages = MessagesControl;
|
|
861
|
+
AIConversation.Form = FormControl;
|
|
902
862
|
return { AIConversation };
|
|
903
863
|
}
|
|
904
864
|
|
|
865
|
+
const PlaceholderMessage = ({ role }) => {
|
|
866
|
+
const variant = React__namespace.useContext(MessageVariantContext);
|
|
867
|
+
return (React__namespace.createElement(uiReact.View, { className: ui.classNames(ui.ComponentClassName.AIConversationMessage, ui.classNameModifier(ui.ComponentClassName.AIConversationMessage, variant), ui.classNameModifier(ui.ComponentClassName.AIConversationMessage, role)) },
|
|
868
|
+
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageAvatar },
|
|
869
|
+
React__namespace.createElement(uiReact.Avatar, null)),
|
|
870
|
+
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageBody },
|
|
871
|
+
React__namespace.createElement(uiReact.Placeholder, { width: "25%" }),
|
|
872
|
+
React__namespace.createElement(uiReact.Placeholder, { width: "50%" }),
|
|
873
|
+
React__namespace.createElement(uiReact.Placeholder, { width: "25%" }))));
|
|
874
|
+
};
|
|
905
875
|
const MessageMeta = ({ message }) => {
|
|
906
876
|
// need to pass this in as props in order for it to be overridable
|
|
907
877
|
const avatars = React__namespace.useContext(AvatarsContext);
|
|
@@ -913,29 +883,30 @@ const MessageMeta = ({ message }) => {
|
|
|
913
883
|
React__namespace.createElement(uiReact.Text, { className: ui.ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username),
|
|
914
884
|
React__namespace.createElement(uiReact.Text, { className: ui.ComponentClassName.AIConversationMessageSenderTimestamp }, getMessageTimestampText(new Date(message.createdAt)))));
|
|
915
885
|
};
|
|
916
|
-
const
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
return (React__namespace.createElement(uiReact.View, { className: ui.
|
|
921
|
-
React__namespace.createElement(uiReact.
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
React__namespace.createElement(uiReact.Text, { className: ui.ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username)))));
|
|
886
|
+
const MessageActions = ({ message }) => {
|
|
887
|
+
const actions = React__namespace.useContext(ActionsContext);
|
|
888
|
+
if (!actions)
|
|
889
|
+
return null;
|
|
890
|
+
return (React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageActions }, actions.map((action, i) => {
|
|
891
|
+
return (React__namespace.createElement(uiReact.Button, { key: i, size: "small", onClick: () => {
|
|
892
|
+
action.handler(message);
|
|
893
|
+
} }, action.component));
|
|
894
|
+
})));
|
|
926
895
|
};
|
|
927
896
|
const Message = ({ message }) => {
|
|
928
897
|
const avatars = React__namespace.useContext(AvatarsContext);
|
|
929
898
|
const variant = React__namespace.useContext(MessageVariantContext);
|
|
899
|
+
const { isLoading } = message;
|
|
930
900
|
const avatar = message.role === 'assistant' ? avatars?.ai : avatars?.user;
|
|
931
901
|
return (React__namespace.createElement(RoleContext.Provider, { value: message.role },
|
|
932
902
|
React__namespace.createElement(uiReact.View, { className: ui.classNames(ui.ComponentClassName.AIConversationMessage, ui.classNameModifier(ui.ComponentClassName.AIConversationMessage, variant), ui.classNameModifier(ui.ComponentClassName.AIConversationMessage, message.role)) },
|
|
933
903
|
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageAvatar },
|
|
934
|
-
React__namespace.createElement(uiReact.Avatar,
|
|
904
|
+
React__namespace.createElement(uiReact.Avatar, { isLoading: isLoading }, avatar?.avatar)),
|
|
935
905
|
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageBody },
|
|
936
906
|
React__namespace.createElement(MessageMeta, { message: message }),
|
|
937
907
|
React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageContent },
|
|
938
|
-
React__namespace.createElement(MessageControl, { message: message }))
|
|
908
|
+
React__namespace.createElement(MessageControl, { message: message })),
|
|
909
|
+
message.role === 'assistant' ? (React__namespace.createElement(MessageActions, { message: message })) : null))));
|
|
939
910
|
};
|
|
940
911
|
const MessageList = ({ messages, }) => {
|
|
941
912
|
const isLoading = React__namespace.useContext(LoadingContext);
|
|
@@ -943,8 +914,10 @@ const MessageList = ({ messages, }) => {
|
|
|
943
914
|
content.text ??
|
|
944
915
|
content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
|
|
945
916
|
return (React__namespace.createElement(uiReact.View, { className: ui.ComponentClassName.AIConversationMessageList },
|
|
946
|
-
|
|
947
|
-
|
|
917
|
+
isLoading ? (React__namespace.createElement(React__namespace.Fragment, null,
|
|
918
|
+
React__namespace.createElement(PlaceholderMessage, { role: "user" }),
|
|
919
|
+
React__namespace.createElement(PlaceholderMessage, { role: "assistant" }))) : null,
|
|
920
|
+
messagesWithRenderableContent.map((message, i) => (React__namespace.createElement(Message, { key: `message-${i}`, message: message })))));
|
|
948
921
|
};
|
|
949
922
|
|
|
950
923
|
const Attachment = ({ file, handleRemove, }) => {
|
|
@@ -990,12 +963,12 @@ const FormWrapper = ({ children, allowAttachments, setInput, }) => {
|
|
|
990
963
|
return children;
|
|
991
964
|
}
|
|
992
965
|
};
|
|
993
|
-
const Form = ({ setInput, input, handleSubmit, allowAttachments, }) => {
|
|
966
|
+
const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) => {
|
|
994
967
|
const icons = internal.useIcons('aiConversation');
|
|
995
968
|
const sendIcon = icons?.send ?? React__namespace.createElement(internal.IconSend, null);
|
|
996
969
|
const attachIcon = icons?.attach ?? React__namespace.createElement(internal.IconAttach, null);
|
|
997
970
|
const hiddenInput = React__namespace.useRef(null);
|
|
998
|
-
const
|
|
971
|
+
const [composing, setComposing] = React__namespace.useState(false);
|
|
999
972
|
const isInputEmpty = !input?.text?.length && !input?.files?.length;
|
|
1000
973
|
return (React__namespace.createElement(FormWrapper, { allowAttachments: allowAttachments, setInput: setInput },
|
|
1001
974
|
React__namespace.createElement(uiReact.View, { as: "form", className: ui.ComponentClassName.AIConversationForm, onSubmit: handleSubmit },
|
|
@@ -1017,9 +990,9 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, }) => {
|
|
|
1017
990
|
files: [...(prevValue?.files ?? []), ...Array.from(files)],
|
|
1018
991
|
}));
|
|
1019
992
|
}, multiple: true, accept: "*", "data-testid": "hidden-file-input" })))) : null,
|
|
1020
|
-
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) => {
|
|
993
|
+
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", onCompositionStart: () => setComposing(true), onCompositionEnd: () => setComposing(false), onKeyDown: (e) => {
|
|
1021
994
|
// Submit on enter key if shift is not pressed also
|
|
1022
|
-
const shouldSubmit = !e.shiftKey && e.key === 'Enter';
|
|
995
|
+
const shouldSubmit = !e.shiftKey && e.key === 'Enter' && !composing;
|
|
1023
996
|
if (shouldSubmit && isHTMLFormElement(e.target)) {
|
|
1024
997
|
e.target.form.requestSubmit();
|
|
1025
998
|
e.preventDefault();
|
|
@@ -1045,89 +1018,69 @@ const PromptList = ({ setInput, suggestedPrompts = [], }) => {
|
|
|
1045
1018
|
...prevInput,
|
|
1046
1019
|
text: prompt.inputText,
|
|
1047
1020
|
}));
|
|
1048
|
-
} }, prompt.
|
|
1021
|
+
} }, prompt.component));
|
|
1049
1022
|
})));
|
|
1050
1023
|
};
|
|
1051
1024
|
|
|
1052
|
-
|
|
1025
|
+
const VERSION = '1.0.0';
|
|
1026
|
+
|
|
1027
|
+
function AIConversationBase({ avatars, controls, ...rest }) {
|
|
1028
|
+
uiReactCore.useSetUserAgent({
|
|
1029
|
+
componentName: 'AIConversation',
|
|
1030
|
+
packageName: 'react-ai',
|
|
1031
|
+
version: VERSION,
|
|
1032
|
+
});
|
|
1053
1033
|
const icons = internal.useIcons('aiConversation');
|
|
1054
1034
|
const defaultAvatars = {
|
|
1055
1035
|
ai: {
|
|
1056
1036
|
username: 'Assistant',
|
|
1057
|
-
avatar: icons?.assistant ?? React__namespace.createElement(internal.IconAssistant,
|
|
1037
|
+
avatar: icons?.assistant ?? React__namespace.createElement(internal.IconAssistant, { testId: "icon-assistant" }),
|
|
1058
1038
|
},
|
|
1059
1039
|
user: {
|
|
1060
1040
|
username: 'User',
|
|
1061
|
-
avatar: icons?.user ?? React__namespace.createElement(internal.IconUser,
|
|
1041
|
+
avatar: icons?.user ?? React__namespace.createElement(internal.IconUser, { testId: "icon-user" }),
|
|
1062
1042
|
},
|
|
1063
1043
|
};
|
|
1064
1044
|
const providerProps = {
|
|
1065
|
-
|
|
1066
|
-
handleSendMessage,
|
|
1045
|
+
...rest,
|
|
1067
1046
|
avatars: {
|
|
1068
1047
|
...defaultAvatars,
|
|
1069
1048
|
...avatars,
|
|
1070
1049
|
},
|
|
1071
|
-
isLoading,
|
|
1072
|
-
elements: {
|
|
1073
|
-
Text: React__namespace.forwardRef(function _Text(props, ref) {
|
|
1074
|
-
return React__namespace.createElement(uiReact.Text, { ...props, ref: ref });
|
|
1075
|
-
}),
|
|
1076
|
-
},
|
|
1077
|
-
actions,
|
|
1078
|
-
suggestedPrompts,
|
|
1079
|
-
responseComponents,
|
|
1080
|
-
variant,
|
|
1081
1050
|
controls: {
|
|
1082
1051
|
MessageList,
|
|
1083
1052
|
PromptList,
|
|
1084
1053
|
Form,
|
|
1085
1054
|
...controls,
|
|
1086
1055
|
},
|
|
1087
|
-
displayText,
|
|
1088
|
-
allowAttachments,
|
|
1089
1056
|
};
|
|
1090
1057
|
return (React__namespace.createElement(AIConversationProvider, { ...providerProps },
|
|
1091
|
-
React__namespace.createElement(uiReact.Flex, { className: ui.ComponentClassName.AIConversation },
|
|
1058
|
+
React__namespace.createElement(uiReact.Flex, { className: ui.ComponentClassName.AIConversation, testId: "ai-conversation" },
|
|
1092
1059
|
React__namespace.createElement(uiReact.ScrollView, { autoScroll: "smooth", flex: "1" },
|
|
1093
|
-
React__namespace.createElement(
|
|
1060
|
+
React__namespace.createElement(DefaultMessageControl, null),
|
|
1094
1061
|
React__namespace.createElement(MessagesControl, null)),
|
|
1095
|
-
React__namespace.createElement(
|
|
1062
|
+
React__namespace.createElement(FormControl, null))));
|
|
1096
1063
|
}
|
|
1097
|
-
/**
|
|
1098
|
-
* @experimental
|
|
1099
|
-
*/
|
|
1100
1064
|
const AIConversation = Object.assign(AIConversationBase, {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1065
|
+
Provider: AIConversationProvider,
|
|
1066
|
+
DefaultMessage: DefaultMessageControl,
|
|
1067
|
+
Messages: MessagesControl,
|
|
1068
|
+
Form: FormControl,
|
|
1104
1069
|
});
|
|
1105
1070
|
|
|
1106
|
-
const AIContext = React__namespace["default"].createContext(undefined);
|
|
1107
|
-
const useAIContext = () => {
|
|
1108
|
-
const context = React__namespace["default"].useContext(AIContext);
|
|
1109
|
-
const [routeToConversationsMap, setRouteToConversationsMap] = React__namespace["default"].useState({});
|
|
1110
|
-
if (context) {
|
|
1111
|
-
return context;
|
|
1112
|
-
}
|
|
1113
|
-
return { routeToConversationsMap, setRouteToConversationsMap };
|
|
1114
|
-
};
|
|
1115
|
-
/**
|
|
1116
|
-
* @experimental
|
|
1117
|
-
*/
|
|
1118
|
-
const AIContextProvider = ({ children, }) => {
|
|
1119
|
-
const context = useAIContext();
|
|
1120
|
-
return React__namespace["default"].createElement(AIContext.Provider, { value: context }, children);
|
|
1121
|
-
};
|
|
1122
|
-
|
|
1123
1071
|
// default state
|
|
1124
1072
|
const INITIAL_STATE = {
|
|
1125
1073
|
hasError: false,
|
|
1126
1074
|
isLoading: false,
|
|
1127
1075
|
messages: undefined,
|
|
1128
1076
|
};
|
|
1129
|
-
const LOADING_STATE = {
|
|
1077
|
+
const LOADING_STATE = {
|
|
1078
|
+
hasError: false,
|
|
1079
|
+
isLoading: true,
|
|
1080
|
+
messages: undefined,
|
|
1081
|
+
};
|
|
1130
1082
|
const ERROR_STATE = { hasError: true, isLoading: false };
|
|
1083
|
+
|
|
1131
1084
|
function createUseAIGeneration(client) {
|
|
1132
1085
|
const useAIGeneration = (routeName) => {
|
|
1133
1086
|
const [dataState, setDataState] = React__namespace.useState(() => ({
|
|
@@ -1154,142 +1107,314 @@ function createUseAIGeneration(client) {
|
|
|
1154
1107
|
return useAIGeneration;
|
|
1155
1108
|
}
|
|
1156
1109
|
|
|
1157
|
-
|
|
1110
|
+
const contentFromEvents = (contentBlocks) => {
|
|
1111
|
+
if (!contentBlocks)
|
|
1112
|
+
return [];
|
|
1113
|
+
return contentBlocks.map((contentBlock) => {
|
|
1114
|
+
const isTextBlock = contentBlock.some((event) => event.text);
|
|
1115
|
+
if (isTextBlock) {
|
|
1116
|
+
return {
|
|
1117
|
+
text: contentBlock
|
|
1118
|
+
.map((event) => {
|
|
1119
|
+
return event.text;
|
|
1120
|
+
})
|
|
1121
|
+
.join(''),
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
// tool use is never chunked
|
|
1125
|
+
if (contentBlock[0].toolUse) {
|
|
1126
|
+
return { toolUse: contentBlock[0].toolUse };
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
async function exhaustivelyListMessages({ conversation, messages = [], nextToken, }) {
|
|
1132
|
+
const result = await conversation.listMessages({ nextToken });
|
|
1133
|
+
if (result.data) {
|
|
1134
|
+
messages?.push(...result.data);
|
|
1135
|
+
}
|
|
1136
|
+
if (result.nextToken) {
|
|
1137
|
+
return exhaustivelyListMessages({
|
|
1138
|
+
conversation,
|
|
1139
|
+
messages,
|
|
1140
|
+
nextToken: result.nextToken,
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1158
1143
|
return {
|
|
1159
|
-
...
|
|
1160
|
-
|
|
1161
|
-
...previousValue[routeName],
|
|
1162
|
-
[conversationId]: messages,
|
|
1163
|
-
},
|
|
1144
|
+
...result,
|
|
1145
|
+
data: messages,
|
|
1164
1146
|
};
|
|
1165
1147
|
}
|
|
1148
|
+
|
|
1149
|
+
function hasStarted(state) {
|
|
1150
|
+
return ['initialLoading', 'initialized'].includes(state);
|
|
1151
|
+
}
|
|
1166
1152
|
function createUseAIConversation(client) {
|
|
1153
|
+
// This is a bit complicated so buckle up.
|
|
1154
|
+
// The way the data client works is conversation.get() or conversation.create()
|
|
1155
|
+
// is an async function because it makes a graphql call to appsync
|
|
1156
|
+
// then it returns a conversation object, which is like a normal
|
|
1157
|
+
// data client record, except that it also has functions on it,
|
|
1158
|
+
// like sendMessage and onStreamEvent. onStreamEvent sets up a
|
|
1159
|
+
// subscription using a websocket connection, which ideally we only want to
|
|
1160
|
+
// do once per conversation. Because we can only subscribe AFTER the
|
|
1161
|
+
// async call to get/create the conversation is made, the cleanup
|
|
1162
|
+
// function in the effect will won't actually unsubscribe
|
|
1167
1163
|
const useAIConversation = (routeName, input = {}) => {
|
|
1168
1164
|
const clientRoute = client.conversations[routeName];
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
const
|
|
1176
|
-
const [
|
|
1177
|
-
|
|
1178
|
-
|
|
1165
|
+
// We need to keep track of the stream events as the come in
|
|
1166
|
+
// for an assistant message, but don't need to keep them in state
|
|
1167
|
+
const contentBlocksRef = React__namespace["default"].useRef();
|
|
1168
|
+
// Using this hook without an existing conversation id means
|
|
1169
|
+
// it will create a new conversation when it is executed
|
|
1170
|
+
// we don't want to create 2 conversations
|
|
1171
|
+
const initRef = React__namespace["default"].useRef('initial');
|
|
1172
|
+
const [dataState, setDataState] = React__namespace["default"].useState(() => ({
|
|
1173
|
+
...INITIAL_STATE,
|
|
1174
|
+
data: { messages: [], conversation: undefined },
|
|
1175
|
+
}));
|
|
1176
|
+
const { conversation } = dataState.data;
|
|
1177
|
+
const { id, onInitialize, onMessage } = input;
|
|
1179
1178
|
React__namespace["default"].useEffect(() => {
|
|
1180
1179
|
async function initialize() {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1180
|
+
// We don't want to run the effect multiple times
|
|
1181
|
+
// because that could create multiple conversation records
|
|
1182
|
+
if (hasStarted(initRef.current))
|
|
1183
|
+
return;
|
|
1184
|
+
initRef.current = 'initialLoading';
|
|
1185
|
+
// Only show component loading state if we are
|
|
1186
|
+
// actually loading messages
|
|
1187
|
+
if (id) {
|
|
1188
|
+
setDataState({
|
|
1189
|
+
...LOADING_STATE,
|
|
1190
|
+
data: { messages: [], conversation: undefined },
|
|
1191
|
+
});
|
|
1189
1192
|
}
|
|
1190
|
-
const { data:
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
messages,
|
|
1193
|
+
const { data: conversation, errors } = id
|
|
1194
|
+
? await clientRoute.get({ id })
|
|
1195
|
+
: await clientRoute.create();
|
|
1196
|
+
if (errors ?? !conversation) {
|
|
1197
|
+
setDataState({
|
|
1198
|
+
...ERROR_STATE,
|
|
1199
|
+
data: { messages: [] },
|
|
1200
|
+
messages: errors,
|
|
1199
1201
|
});
|
|
1202
|
+
}
|
|
1203
|
+
else {
|
|
1204
|
+
if (id) {
|
|
1205
|
+
const { data: messages } = await exhaustivelyListMessages({
|
|
1206
|
+
conversation,
|
|
1207
|
+
});
|
|
1208
|
+
setDataState({
|
|
1209
|
+
...INITIAL_STATE,
|
|
1210
|
+
data: { messages, conversation },
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
setDataState({
|
|
1215
|
+
...INITIAL_STATE,
|
|
1216
|
+
data: { conversation, messages: [] },
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
initRef.current = 'initialized';
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
// this is a runtime guard to make catch an error if
|
|
1223
|
+
// the route name wrong, or there is a mismatch
|
|
1224
|
+
// between the gen2 schema definition and
|
|
1225
|
+
// whats in amplify_outputs
|
|
1226
|
+
if (!clientRoute) {
|
|
1227
|
+
setDataState({
|
|
1228
|
+
...ERROR_STATE,
|
|
1229
|
+
data: { messages: [] },
|
|
1230
|
+
messages: [
|
|
1231
|
+
{
|
|
1232
|
+
message: 'Conversation route does not exist',
|
|
1233
|
+
errorInfo: null,
|
|
1234
|
+
errorType: '',
|
|
1235
|
+
},
|
|
1236
|
+
],
|
|
1200
1237
|
});
|
|
1238
|
+
return;
|
|
1201
1239
|
}
|
|
1202
1240
|
initialize();
|
|
1203
|
-
|
|
1204
|
-
|
|
1241
|
+
return () => {
|
|
1242
|
+
contentBlocksRef.current = undefined;
|
|
1243
|
+
if (hasStarted(initRef.current))
|
|
1244
|
+
return;
|
|
1245
|
+
setDataState({
|
|
1246
|
+
...INITIAL_STATE,
|
|
1247
|
+
data: { messages: [], conversation: undefined },
|
|
1248
|
+
});
|
|
1249
|
+
};
|
|
1250
|
+
}, [clientRoute, id, setDataState]);
|
|
1251
|
+
// Run a separate effect that is triggered by the conversation state
|
|
1252
|
+
// so that we know we have a conversation object to set up the subscription
|
|
1253
|
+
// and also unsubscribe on cleanup
|
|
1205
1254
|
React__namespace["default"].useEffect(() => {
|
|
1206
|
-
if (
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1255
|
+
if (!conversation)
|
|
1256
|
+
return;
|
|
1257
|
+
const subscription = conversation.onStreamEvent({
|
|
1258
|
+
next: (event) => {
|
|
1259
|
+
const {
|
|
1260
|
+
// messages have a content block array,
|
|
1261
|
+
// this is the index of the content block that was updated
|
|
1262
|
+
contentBlockIndex,
|
|
1263
|
+
// this is the index of the content chunk, ensure these are in order!
|
|
1264
|
+
contentBlockDeltaIndex,
|
|
1265
|
+
// this is sent after the last content chunk, verify this matches the
|
|
1266
|
+
// previous contentBlockDeltaIndex
|
|
1267
|
+
contentBlockDoneAtIndex,
|
|
1268
|
+
// this is the final event of the conversation turn
|
|
1269
|
+
stopReason, conversationId, id, } = event;
|
|
1270
|
+
// return early for content blocks being done
|
|
1271
|
+
// or conversation turn being over
|
|
1272
|
+
if (contentBlockDoneAtIndex) {
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
// stop reason will signify end of conversation turn
|
|
1276
|
+
if (stopReason) {
|
|
1277
|
+
// remove loading state from streamed message
|
|
1278
|
+
setDataState((prev) => {
|
|
1279
|
+
return {
|
|
1280
|
+
...prev,
|
|
1281
|
+
data: {
|
|
1282
|
+
...prev.data,
|
|
1283
|
+
messages: prev.data.messages.map((message) => ({
|
|
1284
|
+
...message,
|
|
1285
|
+
isLoading: false,
|
|
1286
|
+
})),
|
|
1287
|
+
},
|
|
1288
|
+
};
|
|
1230
1289
|
});
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
}, [conversation, routeName, setRouteToConversationsMap]);
|
|
1239
|
-
const subscribe = React__namespace["default"].useCallback((handleStoreChange) => {
|
|
1240
|
-
const subscription = conversation &&
|
|
1241
|
-
conversation.onMessage((message) => {
|
|
1242
|
-
if (input.onResponse)
|
|
1243
|
-
input.onResponse(message);
|
|
1244
|
-
setWaitingForAIResponse(false);
|
|
1245
|
-
setLocalMessages((previousLocalMessages) => [
|
|
1246
|
-
...previousLocalMessages,
|
|
1247
|
-
message,
|
|
1248
|
-
]);
|
|
1249
|
-
setRouteToConversationsMap((previousValue) => {
|
|
1250
|
-
return createNewConversationMessageInRoute({
|
|
1251
|
-
previousValue,
|
|
1252
|
-
routeName: routeName,
|
|
1253
|
-
conversationId: conversation.id,
|
|
1254
|
-
messages: [
|
|
1255
|
-
...previousValue[routeName][conversation.id],
|
|
1256
|
-
message,
|
|
1257
|
-
],
|
|
1290
|
+
onMessage?.({
|
|
1291
|
+
id,
|
|
1292
|
+
conversationId,
|
|
1293
|
+
content: contentFromEvents(contentBlocksRef.current),
|
|
1294
|
+
createdAt: new Date().toISOString(),
|
|
1295
|
+
role: 'assistant',
|
|
1296
|
+
isLoading: true,
|
|
1258
1297
|
});
|
|
1298
|
+
// clear out the stream cache
|
|
1299
|
+
contentBlocksRef.current = undefined;
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
// no ref means its the first event for the message stream
|
|
1303
|
+
// so lets create the contentBlocks ref or else we will
|
|
1304
|
+
// add the incoming event to the right content content block
|
|
1305
|
+
if (!contentBlocksRef.current) {
|
|
1306
|
+
contentBlocksRef.current = [[event]];
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
// place the incoming event in the right content block
|
|
1310
|
+
// and order. message content is an array so a single message
|
|
1311
|
+
// can have multiple content blocks, and each content block
|
|
1312
|
+
// can have multiple events/chunks
|
|
1313
|
+
const currentBlock = contentBlocksRef.current[contentBlockIndex];
|
|
1314
|
+
if (!currentBlock) {
|
|
1315
|
+
contentBlocksRef.current[contentBlockIndex] = [event];
|
|
1316
|
+
}
|
|
1317
|
+
else {
|
|
1318
|
+
contentBlocksRef.current[contentBlockIndex] = [
|
|
1319
|
+
...currentBlock.slice(0, contentBlockDeltaIndex),
|
|
1320
|
+
event,
|
|
1321
|
+
...currentBlock.slice(contentBlockDeltaIndex),
|
|
1322
|
+
];
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
setDataState((prev) => {
|
|
1326
|
+
const message = {
|
|
1327
|
+
id,
|
|
1328
|
+
conversationId,
|
|
1329
|
+
content: contentFromEvents(contentBlocksRef.current),
|
|
1330
|
+
createdAt: new Date().toISOString(),
|
|
1331
|
+
role: 'assistant',
|
|
1332
|
+
isLoading: true,
|
|
1333
|
+
};
|
|
1334
|
+
return {
|
|
1335
|
+
...prev,
|
|
1336
|
+
data: {
|
|
1337
|
+
...prev.data,
|
|
1338
|
+
// TODO: we are assuming we only update the last
|
|
1339
|
+
// message, but maybe we should match it by message ID?
|
|
1340
|
+
messages: [...prev.data.messages.slice(0, -1), message],
|
|
1341
|
+
},
|
|
1342
|
+
};
|
|
1259
1343
|
});
|
|
1260
|
-
|
|
1261
|
-
|
|
1344
|
+
},
|
|
1345
|
+
error: (error) => {
|
|
1346
|
+
setDataState((prev) => {
|
|
1347
|
+
return {
|
|
1348
|
+
...prev,
|
|
1349
|
+
...ERROR_STATE,
|
|
1350
|
+
messages: error.errors,
|
|
1351
|
+
};
|
|
1352
|
+
});
|
|
1353
|
+
},
|
|
1354
|
+
});
|
|
1355
|
+
if (ui.isFunction(onInitialize)) {
|
|
1356
|
+
onInitialize(conversation);
|
|
1357
|
+
}
|
|
1262
1358
|
return () => {
|
|
1263
|
-
|
|
1359
|
+
contentBlocksRef.current = undefined;
|
|
1360
|
+
subscription.unsubscribe();
|
|
1264
1361
|
};
|
|
1265
|
-
}, [conversation,
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1362
|
+
}, [conversation, onInitialize, onMessage, setDataState]);
|
|
1363
|
+
const handleSendMessage = React__namespace["default"].useCallback((input) => {
|
|
1364
|
+
const { content } = input;
|
|
1365
|
+
if (conversation) {
|
|
1366
|
+
setDataState((prevState) => ({
|
|
1367
|
+
...prevState,
|
|
1368
|
+
data: {
|
|
1369
|
+
...prevState.data,
|
|
1370
|
+
// optimistically add user and assistant messages
|
|
1371
|
+
messages: [
|
|
1372
|
+
...prevState.data.messages,
|
|
1373
|
+
{
|
|
1374
|
+
content,
|
|
1375
|
+
role: 'user',
|
|
1376
|
+
createdAt: new Date().toISOString(),
|
|
1377
|
+
id: 'temp-id',
|
|
1378
|
+
conversationId: conversation.id ?? '',
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
content: [{ text: ' ' }],
|
|
1382
|
+
role: 'assistant',
|
|
1383
|
+
createdAt: new Date().toISOString(),
|
|
1384
|
+
id: 'temp-id-2',
|
|
1385
|
+
conversationId: conversation.id ?? '',
|
|
1386
|
+
isLoading: true,
|
|
1387
|
+
},
|
|
1388
|
+
],
|
|
1389
|
+
},
|
|
1390
|
+
}));
|
|
1391
|
+
conversation.sendMessage(input);
|
|
1392
|
+
}
|
|
1393
|
+
else {
|
|
1394
|
+
setDataState((prev) => ({
|
|
1395
|
+
...prev,
|
|
1396
|
+
...ERROR_STATE,
|
|
1397
|
+
messages: [
|
|
1398
|
+
{
|
|
1399
|
+
message: 'No conversation found',
|
|
1400
|
+
errorInfo: null,
|
|
1401
|
+
errorType: '',
|
|
1402
|
+
},
|
|
1403
|
+
],
|
|
1404
|
+
}));
|
|
1405
|
+
}
|
|
1406
|
+
}, [conversation]);
|
|
1407
|
+
return [dataState, handleSendMessage];
|
|
1279
1408
|
};
|
|
1280
1409
|
return useAIConversation;
|
|
1281
1410
|
}
|
|
1282
1411
|
|
|
1283
|
-
/**
|
|
1284
|
-
* @experimental
|
|
1285
|
-
*/
|
|
1286
1412
|
function createAIHooks(_client) {
|
|
1287
1413
|
const useAIConversation = createUseAIConversation(_client);
|
|
1288
1414
|
const useAIGeneration = createUseAIGeneration(_client);
|
|
1289
1415
|
return { useAIConversation, useAIGeneration };
|
|
1290
1416
|
}
|
|
1291
1417
|
|
|
1292
|
-
exports.AIContextProvider = AIContextProvider;
|
|
1293
1418
|
exports.AIConversation = AIConversation;
|
|
1294
1419
|
exports.createAIConversation = createAIConversation;
|
|
1295
1420
|
exports.createAIHooks = createAIHooks;
|