TL;DR
Building a great project is the best resume for an aspiring developer.
Well, today we will hit two birds with one stone; I will teach you how to build a cutting-edge, AI-powered application that will generate your resume & cover letter based on your LinkedIn, GitHub & X.
This project & your ensuing resume will blow away any employer.
We'll cover how to:
- Build the resume & cover letter generator web app using Next.js, TypeScript, and Tailwind CSS.
- Use CopilotKit to integrate AI functionalities into the resume & cover letter generator.
- Use Langchain and Tavily to scrape your LinkedIn, GitHub, or X profile content.
CopilotKit: The open-source framework for building in-app AI copilots
CopilotKit is an open-source AI copilot platform. We make it easy to integrate powerful AI into your React apps.
Build:
- ChatBot: Context-aware in-app chatbots that can take actions in-app 💬
- CopilotTextArea: AI-powered textFields with context-aware autocomplete & insertions 📝
- Co-Agents: In-app AI agents that can interact with your app & users 🤖
Prerequisites
To fully understand this tutorial, you need to have a basic understanding of React or Next.js.
Here are the tools required to build the AI-powered resume and cover letter generator:
- React Markdown - a React component that can be given a string of markdown to safely render to React elements.
- Langchain - provides a framework that enables AI agents to search the web, research and scrape any topic or link.
- OpenAI API - provides an API key that enables you to carry out various tasks using ChatGPT models.
- Tavily AI - a search engine that enables AI agents to conduct research or scrape data and access real-time knowledge within the application.
- CopilotKit - an open-source copilot framework for building custom AI chatbots, in-app AI agents, and text areas.
Project Set up and Package Installation
First, create a Next.js application by running the code snippet below in your terminal:
npx create-next-app@latest airesumecoverlettergenerator
Select your preferred configuration settings. For this tutorial, we'll be using TypeScript and Next.js App Router.
Next, install the React Markdown and OpenAI packages with their dependencies.
npm i react-markdown openai
Finally, install the CopilotKit packages. These packages enable us to retrieve data from the React state and add AI copilot to the application.
npm install @copilotkit/react-ui @copilotkit/react-core @copilotkit/backend
Congratulations! You're now ready to build an AI-powered resume and cover letter generator.
Building The Resume & Cover Letter Generator Frontend
In this section, I will walk you through the process of creating the Resume & cover letter generator frontend with static content to define the generator’s user interface.
To get started, go to /[root]/src/app
in your code editor and create a folder called components
. Inside the components folder, create a file named Resume.tsx
In the Resume.tsx
file, add the following code that defines a React functional component called Resume
.
"use client";
// Import React and necessary hooks from the react library
import React from "react";
import { useState } from "react";
// Import the ReactMarkdown component to render markdown content
import ReactMarkdown from "react-markdown";
// Import the Link component from Next.js for navigation
import Link from "next/link";
function Resume() {
// State variables to store the resume and cover letter content
const [coverLetter, setCoverLetter] = useState("");
const [resume, setResume] = useState("");
return (
// Main container with flex layout, full width, and minimum height of screen
<div className="flex flex-col w-full min-h-screen bg-gray-100 dark:bg-gray-800">
{/* Header section with a fixed height, padding, and border at the bottom */}
<header className="flex items-center h-16 px-4 border-b shrink-0 md:px-6 bg-white dark:bg-gray-900">
{/* Link component for navigation with custom styles */}
<Link
href="#"
className="flex items-center gap-2 text-lg font-semibold md:text-base"
prefetch={false}>
<span className="sr-only text-gray-500">Resume Dashboard</span>
<h1>Resume & Cover Letter Generator</h1>
</Link>
</header>
{/* Main content area with padding */}
<main className="flex-1 p-4 md:p-8 lg:p-10">
{/* Container for the content with maximum width and centered alignment */}
<div className="max-w-4xl mx-auto grid gap-8">
{/* Section for displaying the resume */}
<section>
<div className="bg-white dark:bg-gray-900 rounded-lg shadow-sm">
<div className="p-6 md:p-8">
<h2 className="text-lg font-bold">Resume</h2>
<div className="my-6" />
<div className="grid gap-6">
{/* Conditional rendering of the resume content */}
{resume ? (
<ReactMarkdown>{resume}</ReactMarkdown>
) : (
<div>No Resume To Display</div>
)}
</div>
</div>
</div>
</section>
{/* Section for displaying the cover letter */}
<section>
<div className="bg-white dark:bg-gray-900 rounded-lg shadow-sm">
<div className="p-6 md:p-8">
<h2 className="text-lg font-bold">Cover Letter</h2>
<div className="my-6" />
<div className="grid gap-4">
{/* Conditional rendering of the cover letter content */}
{coverLetter ? (
<ReactMarkdown>{coverLetter}</ReactMarkdown>
) : (
<div>No Cover Letter To Display</div>
)}
</div>
</div>
</div>
</section>
</div>
</main>
</div>
);
}
export default Resume;
Next, go to /[root]/src/page.tsx
file, and add the following code that imports Resume
component and defines a functional component named Home
.
import Resume from "./components/Resume";
export default function Home() {
return <Resume />;
}
Finally, run the command npm run dev
on the command line and then navigate to http://localhost:3000/.
Now you should view the resume and cover letter generator frontend on your browser, as shown below.
Congratulations! You're now ready to add AI functionalities to the AI-powered resume and cover letter generator.
Integrating AI Functionalities To The Resume & Cover Letter Generator Using CopilotKit
In this section, you will learn how to add an AI copilot to the Resume & Cover Letter generator to generate resume and cover letter using CopilotKit.
CopilotKit offers both frontend and backend packages. They enable you to plug into the React states and process application data on the backend using AI agents.
First, let's add the CopilotKit React components to the Resume & Cover Letter generator frontend.
Adding CopilotKit to the To-Do List Generator Frontend
Here, I will walk you through the process of integrating the Resume & Cover Letter generator with the CopilotKit frontend to facilitate Resume & Cover Letter generation.
To get started, use the code snippet below to import useCopilotReadable
, and useCopilotAction
, custom hooks at the top of the /src/app/components/Resume.tsx
file.
import { useCopilotAction, useCopilotReadable } from "@copilotkit/react-core";
Inside the Resume
function, below the state variables, add the following code that uses the useCopilotReadable
hook to add the Resume & Cover Letter that will be generated as context for the in-app chatbot. The hook makes the Resume & Cover Letter readable to the copilot.
useCopilotReadable({
description: "The user's cover letter.",
value: coverLetter,
});
useCopilotReadable({
description: "The user's resume.",
value: resume,
});
Below the code above, add the following code that uses the useCopilotAction
hook to set up an action called createCoverLetterAndResume
which will enable the generation of resume and cover letter.
The action takes two parameters called coverLetterMarkdown
and resumeMarkdown
which enables the generation of resume and cover letter. It contains a handler function that generates resume and cover letter based on a given prompt.
Inside the handler function, coverLetter
and resume
states are updated with the newly generated resume and cover letter markdown, as shown below.
useCopilotAction(
{
// Define the name of the action
name: "createCoverLetterAndResume",
// Provide a description for the action
description: "Create a cover letter and resume for a job application.",
// Define the parameters required for the action
parameters: [
{
// Name of the first parameter
name: "coverLetterMarkdown",
// Type of the first parameter
type: "string",
// Description of the first parameter
description:
"Markdown text for a cover letter to introduce yourself and briefly summarize your professional background.",
// Mark the first parameter as required
required: true,
},
{
// Name of the second parameter
name: "resumeMarkdown",
// Type of the second parameter
type: "string",
// Description of the second parameter
description:
"Markdown text for a resume that displays your professional background and relevant skills.",
// Mark the second parameter as required
required: true,
},
],
// Define the handler function to be executed when the action is called
handler: async ({ coverLetterMarkdown, resumeMarkdown }) => {
// Update the state with the provided cover letter markdown text
setCoverLetter(coverLetterMarkdown);
// Update the state with the provided resume markdown text
setResume(resumeMarkdown);
},
},
// Empty dependency array, indicating this effect does not depend on any props or state
[],
);
After that, go to /[root]/src/app/page.tsx
file and import CopilotKit frontend packages and styles at the top using the code below.
import { CopilotKit } from "@copilotkit/react-core";
import { CopilotSidebar } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";
Then use CopilotKit
to wrap the CopilotSidebar
and Resume
components, as shown below. The CopilotKit
component specifies the URL for CopilotKit's backend endpoint (/api/copilotkit/
) while the CopilotSidebar
renders the in-app chatbot that you can give prompts to generate resume and cover letter.
export default function Home() {
return (
<CopilotKit runtimeUrl="/api/copilotkit">
<CopilotSidebar
instructions={"Help the user create a cover letter and resume"}
labels={{
initial:
"Welcome to the cover letter app! Add your LinkedIn, X, or GitHub profile link below.",
}}
defaultOpen={true}
clickOutsideToClose={false}>
<Resume />
</CopilotSidebar>
</CopilotKit>
);
}
After that, run the development server and navigate to http://localhost:3000. You should see that the in-app chatbot was integrated into the Resume and Cover Letter generator.
Adding CopilotKit Backend to the Blog
Here, I will walk you through the process of integrating the Resume and Cover Letter generator with the CopilotKit backend that handles requests from frontend, and provides function calling and various LLM backends such as GPT.
Also, we will integrate an AI agent named Tavily that can scrape content on any given link on the web.
To get started, create a file called .env.local
in the root directory. Then add the environment variables below in the file that hold your ChatGPT
and Tavily
Search API keys.
OPENAI_API_KEY="Your ChatGPT API key"
TAVILY_API_KEY="Your Tavily Search API key"
OPENAI_MODEL=gpt-4-1106-preview
To get the ChatGPT API key, navigate to https://platform.openai.com/api-keys.
To get the Tavily Search API key, navigate to https://app.tavily.com/home
After that, go to /[root]/src/app
and create a folder called api
. In the api
folder, create a folder called copilotkit
.
In the copilotkit
folder, create a file called tavily.ts
and add the following code. The code defines an asynchronous function scrape
that takes a link as input, sends this link to Tavily API, processes the JSON response, and then uses OpenAI's language model to generate a summary of the response in plain English.
// Import the OpenAI library
import OpenAI from "openai";
// Define an asynchronous function named `scrape` that takes a search query string as an argument
export async function scrape(query: string) {
// Send a POST request to the specified API endpoint with the search query and other parameters
const response = await fetch("https://api.tavily.com/search", {
method: "POST", // HTTP method
headers: {
"Content-Type": "application/json", // Specify the request content type as JSON
},
body: JSON.stringify({
api_key: process.env.TAVILY_API_KEY, // API key from environment variables
query, // The search query passed to the function
search_depth: "basic", // Search depth parameter
include_answer: true, // Include the answer in the response
include_images: false, // Do not include images in the response
include_raw_content: false, // Do not include raw content in the response
max_results: 20, // Limit the number of results to 20
}),
});
// Parse the JSON response from the API
const responseJson = await response.json();
// Instantiate the OpenAI class
const openai = new OpenAI();
// Use the OpenAI API to create a completion based on the JSON response
const completion = await openai.chat.completions.create({
messages: [
{
role: "system", // Set the role of the message to system
content: `Summarize the following JSON to answer the research query \`"${query}"\`: ${JSON.stringify(
responseJson
)} in plain English.`, // Provide the JSON response to be summarized
},
],
model: process.env.OPENAI_MODEL || "gpt-4", // Specify the OpenAI model, defaulting to GPT-4 if not set in environment variables
});
// Return the content of the first message choice from the completion response
return completion.choices[0].message.content;
}
Next, create a file called route.ts
in the copilotkit
folder, and add the following code. The code sets up a scraping action using the CopilotKit framework to fetch and summarize content based on a given link.
Then it defines an action that calls the scrape function and returns the result. If the required API key is available, it adds this action to the CopilotKit runtime and responds to a POST request using the OpenAI model specified in the environment variables.
// Import necessary modules and functions
import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/backend";
import { Action } from "@copilotkit/shared";
import { scrape } from "./tavily"; // Import the previously defined scrape function
// Define a scraping action with its name, description, parameters, and handler function
const scrapingAction: Action<any> = {
name: "scrapeContent", // Name of the action
description: "Call this function to scrape content from a url in a query.", // Description of the action
parameters: [
{
name: "query", // Name of the parameter
type: "string", // Type of the parameter
description:
"The query for scraping content. 5 characters or longer. Might be multiple words", // Description of the parameter
},
],
// Handler function to execute when the action is called
handler: async ({ query }) => {
console.log("Scraping query: ", query); // Log the query to the console
const result = await scrape(query); // Call the scrape function with the query and await the result
console.log("Scraping result: ", result); // Log the result to the console
return result; // Return the result
},
};
// Define an asynchronous POST function to handle POST requests
export async function POST(req: Request): Promise<Response> {
const actions: Action<any>[] = []; // Initialize an empty array to store actions
// Check if the TAVILY_API_KEY environment variable is set
if (process.env["TAVILY_API_KEY"]) {
actions.push(scrapingAction); // Add the scraping action to the actions array
}
// Create a new instance of CopilotRuntime with the defined actions
const copilotKit = new CopilotRuntime({
actions: actions,
});
const openaiModel = process.env["OPENAI_MODEL"]; // Get the OpenAI model from environment variables
// Return the response from CopilotKit, using the OpenAIAdapter with the specified model
return copilotKit.response(req, new OpenAIAdapter({ model: openaiModel }));
}
How To Generate Resume and Cover Letter
Now go to the in-app chatbot you integrated earlier and add a LinkedIn, GitHub or X profile link then press enter.
Once you have added the link, the chatbot will use LangChain and Tavily to scrape content from link profile. Then it will use the content to generate a Resume and Cover Letter.
The resume generated should look as shown below.
The cover letter generated should look as shown below.
Congratulations! You’ve completed the project for this tutorial.
Conclusion
You can now build an awesome AI-powered resume generator to both hone in your AI building skills and simplify your job search process!
Please remember to like & save this article if you enjoyed it and let me know what topics you'd want to see covered next.
Top comments (26)
Hope this was a helpful article!
Hi, am just new here
Haven't seen your articles in awhile! This looks like a good one.
Let me know what you think once you've read through it.
Nice blog
Thank you Ankur!
Awesome, this is a great project! 🔥🔥
Thanks David
I love how simple CopilotKit is!
I actually use it for my project 🔥
I love the copilot in Gitroom. It is crazy how simple it is :)
Great project!
Thank you Jose!
Is there a way to access the repo for this?
Yes, I will add it to the article as well, but here it is:
github.com/TheGreatBonnie/airesume...
Ha! nice project
Thanks!
Hit an error
Failed to read clipboard contents: DOMException: Failed to execute 'readText' on 'Clipboard': Read permission denied.
Hello everyone, I hope you're all doing well. I recently launched an open-source project called GraphQLPlaceholder, and I'd love your support. Please check it out and give it a star on GitHub github.com/enochval/graphql-placeh.... Your support would mean a lot to me and help immensely in the project's growth.
Thank you.
I am using gemini api instead of Chatgpt and I've been stuck at 1 error:
Error getting response: TypeError: Cannot read properties of undefined (reading 'slice')
at GoogleGenerativeAIAdapter.getResponse (webpack-internal:///(rsc)/./node_modules/@copilotkit/backend/dist/index.js:862:53)
at CopilotRuntime.getResponse (webpack-internal:///(rsc)/./node_modules/@copilotkit/backend/dist/index.js:551:43)
at CopilotRuntime.response (webpack-internal:///(rsc)/./node_modules/@copilotkit/backend/dist/index.js:589:35)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.