DEV Community

Cover image for Build an AI-Powered Resume & Cover Letter Generator (CopilotKit, LangChain, Tavily & Next.js)
uliyahoo for CopilotKit

Posted on

Build an AI-Powered Resume & Cover Letter Generator (CopilotKit, LangChain, Tavily & Next.js)

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.

Image description

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 🤖

https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3us3vc140aun0dvrdof.gif

Star CopilotKit ⭐️


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
Enter fullscreen mode Exit fullscreen mode

Select your preferred configuration settings. For this tutorial, we'll be using TypeScript and Next.js App Router.

Image description

Next, install the React Markdown and OpenAI packages with their dependencies.

npm i react-markdown openai
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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 />;
}
Enter fullscreen mode Exit fullscreen mode

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.

Image description

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";
Enter fullscreen mode Exit fullscreen mode

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,
  });
Enter fullscreen mode Exit fullscreen mode

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
  [],
);
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

Image description

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
Enter fullscreen mode Exit fullscreen mode

To get the ChatGPT API key, navigate to https://platform.openai.com/api-keys.

Image description

To get the Tavily Search API key, navigate to https://app.tavily.com/home

Image description

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;
}

Enter fullscreen mode Exit fullscreen mode

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 }));
}

Enter fullscreen mode Exit fullscreen mode

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.

Image description

The cover letter generated should look as shown below.

Image description

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 (23)

Collapse
 
uliyahoo profile image
uliyahoo

Hope this was a helpful article!

Collapse
 
jeremiah_the_dev_man profile image
Jeremiah

Haven't seen your articles in awhile! This looks like a good one.

Collapse
 
uliyahoo profile image
uliyahoo

Let me know what you think once you've read through it.

Collapse
 
crown_code_43cc4b866d2688 profile image
Crown Code

Hi, am just new here

Collapse
 
otenbr profile image
José Antonio dos Santos Neto

Great project!

Collapse
 
uliyahoo profile image
uliyahoo

Thank you Jose!

Collapse
 
tyaga001 profile image
Ankur Tyagi

Nice blog

Collapse
 
uliyahoo profile image
uliyahoo

Thank you Ankur!

Collapse
 
nevodavid profile image
Nevo David

I love how simple CopilotKit is!
I actually use it for my project 🔥

Collapse
 
uliyahoo profile image
uliyahoo

I love the copilot in Gitroom. It is crazy how simple it is :)

Collapse
 
justinwells21 profile image
justinwells21

Is there a way to access the repo for this?

Collapse
 
uliyahoo profile image
uliyahoo

Yes, I will add it to the article as well, but here it is:
github.com/TheGreatBonnie/airesume...

Collapse
 
james0123 profile image
James

Ha! nice project

Collapse
 
uliyahoo profile image
uliyahoo

Thanks!

Collapse
 
enoch91 profile image
Enoch Osarenren

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.

Collapse
 
arshadayvid profile image
David Asaolu

Awesome, this is a great project! 🔥🔥

Collapse
 
uliyahoo profile image
uliyahoo

Thanks David

Collapse
 
ferguson0121 profile image
Ferguson

Nice one. Don't think I'd actually submit an AI-generated resume, but this is a cool project.

Collapse
 
uliyahoo profile image
uliyahoo

It's more about the building the project than actually using the resume :)

Collapse
 
time121212 profile image
tim brandom

If I was hiring and saw a resume made by AI, I wouldn't take it as a great sign.

Collapse
 
uliyahoo profile image
uliyahoo

It's mostly about building the project and getting experience building with AI. Being able to build a practical project with cutting edge libraries can be the best resume :)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.