diff --git a/.changeset/seven-bats-travel.md b/.changeset/seven-bats-travel.md new file mode 100644 index 00000000000..03cde47f4d6 --- /dev/null +++ b/.changeset/seven-bats-travel.md @@ -0,0 +1,15 @@ +--- +"@aws-amplify/ui-react-ai": minor +--- + +feat(ai-conversation): add allowAttachments prop + +BREAKING - This is a breaking change to an experimental API. Previously, the AIConversation component always allowed attachments. Now you will need to provide the `allowAttachments` prop to get the same behavior. The reason for this change is that attachments can quickly cost a lot based on the token use and we didn't want the default behavior to have that. + +```jsx + +``` diff --git a/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx b/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx index e043a999150..b71fbecebf9 100644 --- a/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx +++ b/examples/next/pages/ui/components/ai/ai-conversation/index.page.tsx @@ -36,6 +36,7 @@ function Chat() { messages={messages} handleSendMessage={sendMessage} isLoading={isLoading} + allowAttachments suggestedPrompts={[ { inputText: 'hello', diff --git a/packages/react-ai/src/components/AIConversation/AIConversation.tsx b/packages/react-ai/src/components/AIConversation/AIConversation.tsx index b60f3166b07..a5d74c0944a 100644 --- a/packages/react-ai/src/components/AIConversation/AIConversation.tsx +++ b/packages/react-ai/src/components/AIConversation/AIConversation.tsx @@ -30,6 +30,7 @@ function AIConversationBase({ variant, isLoading, displayText, + allowAttachments, }: AIConversationBaseProps): JSX.Element { const icons = useIcons('aiConversation'); const defaultAvatars: Avatars = { @@ -72,6 +73,7 @@ function AIConversationBase({ ...avatars, }, isLoading, + allowAttachments, }; return ( diff --git a/packages/react-ai/src/components/AIConversation/context/AttachmentContext.tsx b/packages/react-ai/src/components/AIConversation/context/AttachmentContext.tsx new file mode 100644 index 00000000000..8a51e7dd928 --- /dev/null +++ b/packages/react-ai/src/components/AIConversation/context/AttachmentContext.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; + +export const AttachmentContext = React.createContext(false); + +export const AttachmentProvider = ({ + children, + allowAttachments, +}: { + children?: React.ReactNode; + allowAttachments?: boolean; +}): JSX.Element => { + return ( + + {children} + + ); +}; diff --git a/packages/react-ai/src/components/AIConversation/context/ControlsContext.tsx b/packages/react-ai/src/components/AIConversation/context/ControlsContext.tsx index 1b13c88c0b7..096c7c4aba8 100644 --- a/packages/react-ai/src/components/AIConversation/context/ControlsContext.tsx +++ b/packages/react-ai/src/components/AIConversation/context/ControlsContext.tsx @@ -7,6 +7,7 @@ export interface ControlsContextProps { Form?: React.ComponentType< { handleSubmit: (e: React.FormEvent) => void; + allowAttachments?: boolean; } & Required >; MessageList?: React.ComponentType<{ messages: ConversationMessage[] }>; diff --git a/packages/react-ai/src/components/AIConversation/createAIConversation.tsx b/packages/react-ai/src/components/AIConversation/createAIConversation.tsx index 58789d2bc22..f894133611a 100644 --- a/packages/react-ai/src/components/AIConversation/createAIConversation.tsx +++ b/packages/react-ai/src/components/AIConversation/createAIConversation.tsx @@ -30,6 +30,7 @@ export function createAIConversation(input: AIConversationInput = {}): { variant, controls, displayText, + allowAttachments, } = input; const Provider = createProvider({ @@ -40,6 +41,7 @@ export function createAIConversation(input: AIConversationInput = {}): { variant, controls, displayText, + allowAttachments, }); function AIConversation(props: AIConversationProps): JSX.Element { diff --git a/packages/react-ai/src/components/AIConversation/createProvider.tsx b/packages/react-ai/src/components/AIConversation/createProvider.tsx index cede94d9dfc..0d0f841eb87 100644 --- a/packages/react-ai/src/components/AIConversation/createProvider.tsx +++ b/packages/react-ai/src/components/AIConversation/createProvider.tsx @@ -17,6 +17,7 @@ import { ResponseComponentsProvider, SendMessageContextProvider, } from './context'; +import { AttachmentProvider } from './context/AttachmentContext'; export default function createProvider({ elements, @@ -26,6 +27,7 @@ export default function createProvider({ variant, controls, displayText, + allowAttachments, }: Pick< AIConversationInput, | 'elements' @@ -35,6 +37,7 @@ export default function createProvider({ | 'variant' | 'controls' | 'displayText' + | 'allowAttachments' >) { return function Provider({ children, @@ -57,25 +60,27 @@ export default function createProvider({ - - - - - - - - - {children} - - - - - - - - + + + + + + + + + + {children} + + + + + + + + + diff --git a/packages/react-ai/src/components/AIConversation/types.ts b/packages/react-ai/src/components/AIConversation/types.ts index 296810be9f0..ba987b8dce1 100644 --- a/packages/react-ai/src/components/AIConversation/types.ts +++ b/packages/react-ai/src/components/AIConversation/types.ts @@ -31,6 +31,7 @@ export interface AIConversationInput { responseComponents?: ResponseComponents; variant?: MessageVariant; controls?: ControlsContextProps; + allowAttachments?: boolean; } export interface AIConversationProps { diff --git a/packages/react-ai/src/components/AIConversation/views/Controls/AttachFileControl.tsx b/packages/react-ai/src/components/AIConversation/views/Controls/AttachFileControl.tsx index 40338cfc17f..2d6558176a5 100644 --- a/packages/react-ai/src/components/AIConversation/views/Controls/AttachFileControl.tsx +++ b/packages/react-ai/src/components/AIConversation/views/Controls/AttachFileControl.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { withBaseElementProps } from '@aws-amplify/ui-react-core/elements'; import { ConversationInputContext } from '../../context'; import { AIConversationElements } from '../../context/elements'; +import { useDropZone } from '@aws-amplify/ui-react-core'; const { Button, Icon, View } = AIConversationElements; @@ -32,6 +33,17 @@ const AttachFileButton = withBaseElementProps(Button, { export const AttachFileControl: AttachFileControl = () => { const hiddenInput = React.useRef(null); const { setInput } = React.useContext(ConversationInputContext); + const { dragState, ...dropHandlers } = useDropZone({ + acceptedFileTypes: ['.jpeg'], + onDropComplete: ({ acceptedFiles }) => { + if (acceptedFiles && acceptedFiles?.length > 0 && setInput) { + setInput((prevInput) => ({ + ...prevInput, + files: [...(prevInput?.files ?? []), ...acceptedFiles], + })); + } + }, + }); function handleButtonClick() { if (hiddenInput.current) { @@ -53,7 +65,7 @@ export const AttachFileControl: AttachFileControl = () => { } return ( - + diff --git a/packages/react-ai/src/components/AIConversation/views/Controls/FieldControl.tsx b/packages/react-ai/src/components/AIConversation/views/Controls/FieldControl.tsx index d44b1e0606c..cc9e012362c 100644 --- a/packages/react-ai/src/components/AIConversation/views/Controls/FieldControl.tsx +++ b/packages/react-ai/src/components/AIConversation/views/Controls/FieldControl.tsx @@ -15,6 +15,7 @@ import { import { ControlsContext } from '../../context/ControlsContext'; import { getImageTypeFromMimeType } from '../../utils'; import { LoadingContext } from '../../context/LoadingContext'; +import { AttachmentContext } from '../../context/AttachmentContext'; const { Button, @@ -148,6 +149,7 @@ const InputContainer = withBaseElementProps(View, { export const FieldControl: FieldControl = () => { const { input, setInput } = React.useContext(ConversationInputContext); const handleSendMessage = React.useContext(SendMessageContext); + const allowAttachments = React.useContext(AttachmentContext); const ref = React.useRef(null); const responseComponents = React.useContext(ResponseComponentsContext); const controls = React.useContext(ControlsContext); @@ -212,6 +214,7 @@ export const FieldControl: FieldControl = () => { handleSubmit={handleSubmit} input={input!} setInput={setInput!} + allowAttachments={allowAttachments} /> ); } @@ -223,7 +226,7 @@ export const FieldControl: FieldControl = () => { method="post" ref={ref} > - + {allowAttachments ? : null}