Building an AI Project Step By Step Hands-On Project and Deploying on AWS
In this article, we will build an end-to-end AI project for coaching on resumes. We will build the UI using React and deploy it on S3. We will use Llama 3.2 1B instruct and deploy it on AWS EC2. Finally, we will create the Core Service using LangChain and FastAPIs to receive the request from the UI and route it to the LLMs. Those are the basic building blocks for any AI application. We will keep growing this project by adding more services, more LLM providers, and RAG. I will point to other articles for each step to avoid crowding the article.
Step 1: Deploying the Llama 3.2 1B Instruct model on an AWS EC2 instance and exposing it as an API endpoint
In the following article, I’ll take you through the complete process of deploying the Llama 3.2 1B Instruct model on an AWS EC2 instance and exposing it as an API endpoint. Whether you’re just starting out with cloud-hosted AI or looking to enhance your deployment skills, this guide will provide step-by-step instructions on setting up your environment, downloading and testing the model, and creating a FastAPI endpoint for seamless interaction. By the end, you’ll have a fully operational setup capable of handling AI-powered tasks and ready to integrate with any application or user interface.
Note: The maximum context length for Llama 3.2 1B Instruct is typically 4,096 tokens. This means the model can process up to 4,096 tokens, including both the input tokens and the tokens generated as output. You will need to summarize the resume and JD to void exceeding the context limit.
Step 2: Building a LangChain-Powered FastAPI Service to Optimize LLM Interactions
In the following article, we will build a LangChain FastAPI service to connect to the LLM service. You may deploy it in its own EC2 instance.
Step3: Accessing it from the UI
Follow the article below to build a UI that can access the LLM
Change the App.js to be as follows:
import React, {
useState,
} from 'react';
import {
Button,
TextField,
Typography,
Container,
Box,
MenuItem,
Select,
FormControl,
InputLabel,
} from '@mui/material';
import axios from 'axios';
function App() {
const [resume, setResume] =
useState(null);
const [
jobDescription,
setJobDescription,
] = useState('');
const [apiKey, setApiKey] =
useState('');
const [
selectedOption,
setSelectedOption,
] = useState('openai'); // Default to OpenAI
const [
coachingAdvice,
setCoachingAdvice,
] = useState('');
const [
sagemakerEndpoint,
setSageMakerEndpoint,
] = useState('');
// Handle resume file selection
const handleResumeChange = (
event
) => {
setResume(
event.target.files[0]
);
};
// Handle job description input
const handleJobDescriptionChange =
(event) => {
setJobDescription(
event.target.value
);
};
// Handle API key input
const handleApiKeyChange = (
event
) => {
setApiKey(
event.target.value
);
};
// Handle SageMaker endpoint input
const handleSageMakerEndpointChange =
(event) => {
setSageMakerEndpoint(
event.target.value
);
};
// Handle option selection
const handleOptionChange = (
event
) => {
setSelectedOption(
event.target.value
);
};
// Submit the files and get coaching advice
const handleSubmit =
async () => {
if (
!resume ||
!jobDescription
) {
alert(
'Please provide a resume and job description.'
);
return;
}
if (
selectedOption ===
'openai' &&
!apiKey
) {
alert(
'Please provide your OpenAI API key.'
);
return;
}
try {
const reader =
new FileReader();
reader.onload =
async () => {
const resumeText =
reader.result;
const prompt = `Resume: \n${resumeText}\n\nJob Description: \n${jobDescription}`;
let response;
if (
selectedOption ===
'openai'
) {
// OpenAI API
const requestBody =
{
model:
'gpt-4',
messages: [
{
role: 'system',
content:
'You are a resume coach. Analyze the resume and job description provided and give actionable advice for improvement.',
},
{
role: 'user',
content:
prompt,
},
],
};
response =
await axios.post(
'https://api.openai.com/v1/chat/completions',
requestBody,
{
headers: {
'Content-Type':
'application/json',
Authorization: `Bearer ${apiKey}`,
},
}
);
setCoachingAdvice(
response.data
.choices[0]
.message
.content
);
} else if (
selectedOption ===
'llama'
) {
// Llama 3.2 1B Instruct API
response =
await axios.post(
'http://54.152.246.69:8000/generate',
{ prompt },
{
headers: {
'Content-Type':
'application/json',
},
}
);
setCoachingAdvice(
response.data
.response
);
} else if (
selectedOption ===
'sagemaker'
) {
// SageMaker API
response =
await axios.post(
sagemakerEndpoint,
{
inputs:
prompt,
},
{
headers: {
'Content-Type':
'application/json',
},
}
);
setCoachingAdvice(
response.data
.body
.generated_text
);
}
};
reader.readAsText(
resume
);
} catch (error) {
console.error(
'Error:',
error
);
setCoachingAdvice(
'Error fetching coaching advice. Please try again.'
);
}
};
return (
<Container
maxWidth='sm'
style={{
marginTop: '20px',
}}>
<Typography
variant='h4'
align='center'
gutterBottom>
Resume Coach
</Typography>
<Box marginBottom='20px'>
<Typography variant='h6'>
Select Processing
Option:
</Typography>
<FormControl
fullWidth>
<InputLabel id='option-select-label'>
Option
</InputLabel>
<Select
labelId='option-select-label'
value={
selectedOption
}
onChange={
handleOptionChange
}>
<MenuItem value='openai'>
OpenAI API
</MenuItem>
<MenuItem value='llama'>
Llama 3.2 1B
Instruct
</MenuItem>
<MenuItem value='sagemaker'>
SageMaker
Endpoint
</MenuItem>
</Select>
</FormControl>
</Box>
{selectedOption ===
'openai' && (
<Box marginBottom='20px'>
<Typography variant='h6'>
OpenAI API Key:
</Typography>
<TextField
fullWidth
type='password'
placeholder='Enter your OpenAI API key'
value={apiKey}
onChange={
handleApiKeyChange
}
/>
</Box>
)}
{selectedOption ===
'sagemaker' && (
<Box marginBottom='20px'>
<Typography variant='h6'>
SageMaker Endpoint
URL:
</Typography>
<TextField
fullWidth
placeholder='Enter your SageMaker endpoint URL'
value={
sagemakerEndpoint
}
onChange={
handleSageMakerEndpointChange
}
/>
</Box>
)}
<Box marginBottom='20px'>
<Typography variant='h6'>
Upload Your Resume:
</Typography>
<input
type='file'
accept='.pdf,.docx,.txt'
onChange={
handleResumeChange
}
style={{
marginTop: '10px',
}}
/>
</Box>
<Box marginBottom='20px'>
<Typography variant='h6'>
Paste Job
Description:
</Typography>
<TextField
multiline
rows={6}
fullWidth
variant='outlined'
placeholder='Paste the job description here...'
value={
jobDescription
}
onChange={
handleJobDescriptionChange
}
/>
</Box>
<Button
variant='contained'
color='primary'
onClick={handleSubmit}
fullWidth
style={{
marginBottom:
'20px',
}}>
Get Coaching Advice
</Button>
{coachingAdvice && (
<Box>
<Typography variant='h6'>
Coaching Advice:
</Typography>
<Typography
variant='body1'
style={{
whiteSpace:
'pre-line',
}}>
{coachingAdvice}
</Typography>
</Box>
)}
</Container>
);
}
export default App;
When you run the UI using npm start, you should see the following
You can test Calling the Llama model from here. Just make sure to use a short resume and a short JD because of the context limit. This is the problem we fixed in the previous article with the LangChain service
You can follow the article to deploy it to S3.