const isMessageAIGenerated = (message: LocalMessage) => !!message.ai_generated;SDK Integration
The AI components work seamlessly with the React Native SDK.
StreamingMessageView
Integrating the StreamingMessageView within the SDK is relatively easy.
The SDK already comes prebuilt with a isMessageAIGenerated factory function that lets the underlying contexts know when we consider a message to be AI generated and renders its internal StreamingMessageView whenever this is true.
Let us say that a message is considered AI generated if the message.ai_generated property is set to true.
In that case, our factory function would look something like this:
Then, we can simply pass our new StreamingMessageView like so:
import { StreamingMessageView } from "@stream-io/chat-react-native-ai";
const CustomStreamingMessageView = () => {
const { message } = useMessageContext();
return (
<View style={{ width: "100%", paddingHorizontal: 16 }}>
<StreamingMessageView text={message.text ?? ""} />
</View>
);
};
// ...
<Channel
{...otherChannelProps}
StreamingMessageView={CustomStreamingMessageView}
/>;and this will create a full width message that uses the StreamingMessageView from @stream-io/chat-react-native-ai instead of the default one.
ComposerView
To use the ComposerView, you’ll only really need to create a resolver for the purposes of sending a message and then use it instead of our standard MessageInput.
That would look something like this:
import { ComposerView } from "@stream-io/chat-react-native-ai";
const CustomComposerView = () => {
const messageComposer = useMessageComposer();
const { sendMessage } = useMessageInputContext();
const { channel } = useChannelContext();
const { aiState } = useAIState(channel);
const stopGenerating = useCallback(
() => channel?.stopAIResponse(),
[channel],
);
const isGenerating = [AIStates.Thinking, AIStates.Generating].includes(
aiState,
);
const serializeToMessage = useStableCallback(
async ({ text, attachments }: { text: string; attachments?: any[] }) => {
messageComposer.textComposer.setText(text);
if (attachments && attachments.length > 0) {
const localAttachments = await Promise.all(
attachments.map((a) =>
messageComposer.attachmentManager.fileToLocalUploadAttachment(a),
),
);
messageComposer.attachmentManager.upsertAttachments(localAttachments);
}
await sendMessage();
},
);
return (
<ComposerView
onSendMessage={serializeToMessage}
isGenerating={isGenerating}
stopGenerating={stopGenerating}
/>
);
};What this does is the following:
- It accepts the
textandattachmentprops and sets them to ourmessageComposerstate, throughmessageComposer.setTextandmessageComposer.upsertAttachments - It invokes
sendMessagefrom ourMessageInputContextonce the composer is updated and let the SDK handle all the rest - It also passes
isGeneratingandstopGeneratingto the composer so we also get the stop generating button
AITypingIndicatorView
You can also add thinking indicators, for example just below the MessageList and just above the ComposerView (or MessageInput).
Doing that would look something like this:
import { AITypingIndicatorView } from "@stream-io/chat-react-native-ai";
const CustomAIThinkingIndicatorView = () => {
const { channel } = useChannelContext();
const { aiState } = useAIState(channel);
const allowedStates = {
[AIStates.Thinking]: "Thinking about the question...",
[AIStates.Generating]: "Generating a response...",
[AIStates.ExternalSources]: "Checking external sources...",
};
if (aiState === AIStates.Idle || aiState === AIStates.Error) {
return null;
}
return (
<View
style={{
paddingHorizontal: 24,
paddingVertical: 12,
}}
>
<AITypingIndicatorView text={allowedStates[aiState]} />
</View>
);
};
// ...
<Channel {...channelProps}>
<MessageList />
<CustomAIThinkingIndicatorView />
<CustomComposerView />
</Channel>;