AI Cover letter generator app

Develop AI Cover letter generator app in Flask using Gemini API

This post is part of the GenAI Series.

In the previous post, I discussed creating a recipe generator AI app in PHP using OpenAI APIs. In this post, I will explain how to use Google’s Gemini LLM APIs to create another text-based AI app: an AI cover letter generator. Let’s dive in!

If you are in a hurry you may check the demo video below:

I introduced you to Google’s LLM API recently. At that time, it was called Bard, which was later rebranded to Gemini. I automated a WordPress blog with Bard APIs to create financial posts.

API Key Generation

To use Gemini APIs you need an API Key, for that, you will have to visit AI Studio.

Google AI Studio

Once the API Key is generated it will be available for you:

Gemini API Key

Setting up Python SDK

Although you can use Gemini’s REST APIs with the help of  requests library but I will be using the official Python SDK provided by Google. Run the following pip command:

pip install -U google-generativeai

Setting up Python Flask

Since the app is a web app, I am going to use Python Flask for this purpose. If it is not installed you may install it by running the following command in the terminal:

pip install --upgrade flask

Development

Once the required libraries are installed, it’s time to start working on the app. There are three major parts of any AI generator app:

  • Prompt:  The instructions that will be passed to an LLM to produce the required output. You can usually test it out in an LLM chat interface (ChatGPT, Gemini, Claude) without using any API.
  • User Interface:  This could be either web, mobile, or even CLI.
  • LLM API Integration:  Integration of APIs so that you can use the prompt’s output in your app.

Prompt Engineering

So, we are going to perform prompt engineering: writing and optimizing a prompt to suit our needs.

Head over to Gemini and test the following prompt:

You are a skilled freelancer with expertise in writing cover letters that have successfully helped you secure many jobs. Your task is to craft a compelling cover letter that persuades the client to contact you. It should utilize persuasive techniques and convey that the freelancer is focused on delivering results and helping achieve goals, rather than solely pursuing monetary gain.

You will be provided with information in JSON format, which will serve as the foundation for constructing the cover letter

“`
{
“Name”: “Adnan Siddiqi”,
“JobTitle”: “AI Engineer”,
“PrimarySkills”: [“OpenAI”, “Prompt Engineering”, “LLMs”, “Python”],
“Experience”: 7,
“ClientJobPost”: “Needs to hire 2 Freelancers\nI’m looking for a tech lead to help us with the technical aspects of our AI and full-stack projects.\n\nThe work would involve\n- Creating tech architecture, designs and plans\n- Working with the dev team and unblocking them on the tech aspect\n- Preparing project estimates\n- Client meetings and communication\n- Daily team huddles\n\nYou are someone\n- You are a full time freelancer (no full time job or agency at this time)\n- With 8-10+ years of tech experience\n- Hands-on if required on Python, Django, Fast, React,\n- Solution Architect (AWS, Google, Azure) – one cloud is fine\n- Have knowledge of AI, GPT, LLM and Agents – this is a bonus\n- Write \”tech lead\” on top so I know you have read till here\n\nWe are starting on new projects and our existing tech lead is busy with some of our other projects.”,
“RelevantProjects”: [“AI Recipe Generator”],
“ClientName”: “Acme Inc”,
“StartDate”: “2024-11-21”,
“Deadline”: “2024-12-28”,
“Tone”: “conversational”
}
“`

The output must be in JSON format contain the coverletter text in “text” field.

I have used this UpWork job post as an example for testing the prompt. When you run the prompt it prints:

{
  "text": "Hi Acme Inc,\n\nI'm Adnan Siddiqi, an AI Engineer with 7 years of experience. I'm writing to express my keen interest in the tech lead position you've posted. I'm a full-time freelancer, ready to dedicate my skills and expertise to your AI and full-stack projects.\n\nI've been closely following the advancements in AI, particularly in the areas of OpenAI, Prompt Engineering, and LLMs. My experience in Python and my hands-on approach to problem-solving make me confident in my ability to deliver exceptional results. \n\nI've successfully led teams in the past and have a proven track record of creating innovative solutions. I'm excited about the opportunity to contribute to your projects, from crafting robust tech architectures to collaborating seamlessly with your dev team. I'm also proficient in project estimation and client communication, ensuring that projects are delivered on time and within budget.\n\nI'm particularly interested in the AI Recipe Generator project. I believe my skills in AI and Python can be leveraged to create a truly innovative and user-friendly solution. \n\nI'm eager to discuss how my skills and experience can benefit your team. Let's connect to explore this opportunity further.\n\nThanks,\nAdnan Siddiqi"
}

Looks good!  At the UI end, we will be parsing the JSON extracting the data from the text field and displaying it.

UI Design

This is a simple bootstrap CSS-based web form that accepts different inputs. Below is the Flask code for the relevant route:

@app.route('/submit', methods=['POST'])
def cover_letter_form():
    if request.method == 'POST':
        COVER_LETTER_PROMPT = """
        You are a skilled freelancer with expertise in writing cover letters that have successfully helped you secure many jobs. Your task is to craft a compelling cover letter that persuades the client to contact you. It should utilize persuasive techniques and convey that the freelancer is focused on delivering results and helping achieve goals, rather than solely pursuing monetary gain.

        You will be provided with information in JSON format, which will serve as the foundation for constructing the cover letter:

        {{
          "Name": "{name}",
          "JobTitle": "{jobTitle}",
          "PrimarySkills": {primarySkills},
          "Experience": {experience},
          "ClientJobPost": "{clientJobPost}",
          "RelevantProjects": {relevantProjects},
          "ClientName": "{clientName}",
          "StartDate": "{startDate}",
          "Deadline": "{deadline}",
          "Tone": "{tone}"
        }}
        """
        data = request.get_json()
        # Format the prompt with the data
        formatted_prompt = COVER_LETTER_PROMPT.format(
            name=data['name'],
            jobTitle=data['jobTitle'],
            primarySkills=str(data['primarySkills']),  # Convert list to string
            experience=data['experience'],
            clientJobPost=data['clientJobPost'].replace("\n", "\\n"),  # Escape newlines
            relevantProjects=str(data['relevantProjects']),  # Convert list to string
            clientName=data['clientName'],
            startDate=data['startDate'],
            deadline=data['deadline'],
            tone=data['tone']
        )

        generation_config = {
            "response_schema": content.Schema(
                type=content.Type.OBJECT,
                properties={
                    "answer": content.Schema(
                        type=content.Type.STRING,
                    ),
                },
            ),
            "response_mime_type": "application/json",
        }
        genai.configure(api_key=os.environ["GEMINI_KEY"])
        model = genai.GenerativeModel(
            'gemini-1.5-flash',
            generation_config=generation_config
        )
        response = model.generate_content(formatted_prompt)
        # Example: Print the received data (replace with processing logic)
        print(f"Name: {data.get('name')}")
        print(f"Job Title: {data.get('jobTitle')}")
        print(f"Primary Skills: {data.get('primarySkills')}")
        print(f"Experience: {data.get('experience')}")
        print(f"Client Job Post: {data.get('clientJobPost')}")
        print(f"Relevant Projects: {data.get('relevantProjects')}")
        print(f"Client Name: {data.get('clientName')}")
        print(f"Start Date: {data.get('startDate')}")
        print(f"Deadline: {data.get('deadline')}")
        print(f"Tone: {data.get('tone')}")
        return jsonify(response.text)

Calling the generate_content method with the required parameters it returns the JSON payload. In the /submit route it is returning JSON data that is later called via AJAX

$.ajax({
                    url: '/submit', // Adjust URL as needed
                    type: 'POST',
                    contentType: 'application/json',
                    data: JSON.stringify(formData),
                    success: function(response) {
                        const data = JSON.parse(response)
                        let answer = data.answer
                        answer = answer.replace(/\n/g, '<br>')
                        $('#responseOutput').html(answer)
                        $('#responseContainer').show();
                        $("#wait").hide()
                    },
                    error: function(xhr, status, error) {
                        $('#responseOutput').text(`Error: ${xhr.responseText}`);
                        $("#wait").hide()
                        $('#responseContainer').show();

                    }
                });

Simple yet amazing, No?

Conclusion

In this post, we explored how you can integrate Google Gemini LLM APIs with your app to add intelligence to it. As always, the code is available on GitHub.

Looking to create something similar or even more exciting? Schedule a meeting or email me at kadnan @ gmail.com.

If you like this post then you should subscribe to my blog for future updates.

* indicates required