Set up AI
In this guide, you will learn how to get stared with the Amplify AI kit. This includes defining your AI backend with Conversation and Generation routes, and securely connecting to them from your frontend application.
Prerequisites
Before you begin, you will need:
You will also need an AWS account that is setup for local development and has access to the Bedrock Foundation Model(s) you want to use. You can request access to Bedrock models by going in to the Bedrock console and requesting access.
Create an Amplify backend
Run the create amplify script in your project directory:
npm create amplify@latest
Then run the Amplify sandbox to start your local cloud sandbox:
npx ampx sandbox
This will provision the cloud resources you define in your amplify folder and watch for updates and redeploy them.
Build your AI backend
To build an AI backend, you define AI 'routes' in your Amplify Data schema. An AI route is like an API endpoint for interacting with backend AI functionality. There are currently 2 types of routes:
- Conversation: A conversation route is a streaming, multi-turn API. Conversations and messages are automatically stored in DynamoDB so users can resume conversations. Examples of this are any chat-based AI experience or conversational UI.
- Generation: A single synchronous request-response API. A generation route is just an AppSync Query. Examples of this are: generating alt text for an image, generating structured data from unstructured input, summarization, etc.
To define AI routes, open your amplify/data/resource.ts file and use a.generation()
and a.conversation()
in your schema.
import { a, defineData, type ClientSchema } from '@aws-amplify/backend';
const schema = a.schema({ // This will add a new conversation route to your Amplify Data backend. chat: a.conversation({ aiModel: a.ai.model('Claude 3 Haiku'), systemPrompt: 'You are a helpful assistant', }) .authorization((allow) => allow.owner()),
// This adds a new generation route to your Amplify Data backend. generateRecipe: a.generation({ aiModel: a.ai.model('Claude 3 Haiku'), systemPrompt: 'You are a helpful assistant that generates recipes.', }) .arguments({ description: a.string(), }) .returns( a.customType({ name: a.string(), ingredients: a.string().array(), instructions: a.string(), }) ) .authorization((allow) => allow.authenticated()),});
If you have the Amplify sandbox running, when you save this file it will pick up the changes and redeploy the necessary resources for you.
Connect your frontend
Once the cloud sandbox is up and running, it will also create an amplify_outputs.json
file, which includes relevant connection information to your AI routes and other Amplify configuration.
To connect your frontend code to your backend, you need to:
- Configure the Amplify library with the Amplify client configuration file (
amplify_outputs.json
). - Generate a new API client from the Amplify library.
- Make an API request with end-to-end type-safety.
Install the client libraries
Install the Amplify client library to your project:
npm add aws-amplify @aws-amplify/ui-react @aws-amplify/ui-react-ai
Configure the libraries
import "@/styles/app.css";import type { AppProps } from "next/app";import { Amplify } from "aws-amplify";import outputs from "@/amplify_outputs.json";import "@aws-amplify/ui-react/styles.css";
Amplify.configure(outputs);
export default function App({ Component, pageProps }: AppProps) { return <Component {...pageProps} />;}
Create a client component that will configure Amplify:
"use client";
import { Amplify } from "aws-amplify";import config from "@/../amplify_outputs.json";import "@aws-amplify/ui-react/styles.css";
Amplify.configure(config);
export const ConfigureAmplify = () => { return null;};
Then render that component in the root layout:
import { ConfigureAmplify } from "./ConfigureAmplify";
export default function RootLayout({ children,}: Readonly<{ children: React.ReactNode;}>) { return ( <html lang="en"> <body> <ConfigureAmplify /> {children} </body> </html> );}
Generate the data client
Next, generate a type-safe frontend client to talk to our backend using our backend data schema and the generateClient()
function provided by the Amplify libraries.
It can be helpful to create a client.ts/js
file that exports the generated Amplify data client as well as the generated React hooks.
import { generateClient } from "aws-amplify/api";import { Schema } from "../amplify/data/resource";import { createAIHooks } from "@aws-amplify/ui-react-ai";
export const client = generateClient<Schema>({ authMode: "userPool" });export const { useAIConversation, useAIGeneration } = createAIHooks(client);
import { generateClient } from "aws-amplify/api";import { Schema } from "../amplify/data/resource";import { createAIHooks } from "@aws-amplify/ui-react-ai";
/** * @type {import('aws-amplify/data').Client<import('../amplify/data/resource').Schema>} */export const client = generateClient({ authMode: "userPool" });export const { useAIConversation, useAIGeneration } = createAIHooks(client);
Use a generation
import { Flex, TextAreaField, Loader, Text, View } from "@aws-amplify/ui-react"import { useAIConversation } from "@/client";
export default function Page() { const [description, setDescription] = React.useState(""); const [{ data, isLoading, hasError }, generateRecipe] = useAIGeneration("generateRecipe");
const handleClick = async () => { generateRecipe({ description }); };
return ( <Flex direction="column"> <Flex direction="row"> <TextAreaField autoResize value={description} onChange={(e) => setDescription(e.target.value)} label="Description" /> <Button onClick={handleClick}>Generate recipe</Button> </Flex> {isLoading ? ( <Loader variation="linear" /> ) : ( <> <Text fontWeight="bold">{data?.name}</Text> <View as="ul"> {data?.ingredients?.map((ingredient) => ( <View as="li" key={ingredient}> {ingredient} </View> ))} </View> <Text>{data?.instructions}</Text> </> )} </Flex> );}
'use client'import { useAIConversation } from "@/client";
export default function Page() { const [description, setDescription] = React.useState(""); const [{ data, isLoading, hasError }, generateRecipe] = useAIGeneration("generateRecipe");
const handleClick = () => { generateRecipe({ description }); };
return ( <Flex direction="column"> <Flex direction="row"> <TextAreaField autoResize value={description} onChange={(e) => setDescription(e.target.value)} label="Description" /> <Button onClick={handleClick}>Generate recipe</Button> </Flex> {isLoading ? ( <Loader variation="linear" /> ) : ( <> <Heading level={2}>{data?.name}</Heading> <View as="ul"> {data?.ingredients?.map((ingredient) => ( <Text as="li" key={ingredient}> {ingredient} </Text> ))} </View> <Text>{data?.instructions}</Text> </> )} </Flex> );}
Use a conversation
AI conversations are scoped to a user, so your users will need to be logged in with Amplify auth. The easiest way to do this is with the Authenticator component.
import { Authenticator } from "@aws-amplify/ui-react";import { AIConversation } from '@aws-amplify/ui-react-ai';import { useAIConversation } from "@/client";
export default function Page() { const [ { data: { messages }, isLoading, }, handleSendMessage, ] = useAIConversation('chat'); // 'chat' is based on the key for the conversation route in your schema.
return ( <Authenticator> <AIConversation messages={messages} isLoading={isLoading} handleSendMessage={handleSendMessage} /> </Authenticator> );}
'use client'import { Authenticator } from "@aws-amplify/ui-react";import { AIConversation } from '@aws-amplify/ui-react-ai';import { useAIConversation } from "@/client";
export default function Page() { const [ { data: { messages }, isLoading, }, handleSendMessage, ] = useAIConversation('chat'); // 'chat' is based on the key for the conversation route in your schema.
return ( <Authenticator> <AIConversation messages={messages} isLoading={isLoading} handleSendMessage={handleSendMessage} /> </Authenticator> );}