Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# For VS Code Python extension
PYTHONPATH=backend/src
GEMINI_API_KEY=AIzaSyA-id6fL6AZ84X_NE3OzoLiFSBys4aWDD8
GEMINI_MODEL=gemini-2.0-flash
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.venv
**/.env
**/__pycache__/
.env
2 changes: 1 addition & 1 deletion backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ don't conflict with the ones on your main system by running:
### macOS/Unix

```bash
$ python3 -m venv .venv
$ python3 -m venv .venbash
$ source .venv/bin/activate
```

Expand Down
Binary file modified backend/requirements.txt
Binary file not shown.
126 changes: 102 additions & 24 deletions backend/src/api.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,109 @@
"""
This file defines the FastAPI app for the API and all of its routes.
To run this API, use the FastAPI CLI
$ fastapi dev src/api.py
"""
from __future__ import annotations
import io
import os
import re
import json
import textwrap
import uuid
from typing import List, Dict, Optional
import fitz

import random
from dotenv import load_dotenv
load_dotenv() # load .env into os.environ

from fastapi import FastAPI
#import google.generativeai as genai
from anyio import to_thread
from google import genai

# The app which manages all of the API routes
app = FastAPI()
from fastapi import FastAPI, UploadFile, File, Form, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, StreamingResponse
from pydantic import BaseModel

from pypdf import PdfReader
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas

# The decorator declares the function as a FastAPI route on the given path.
# This route in particular is a GET route at "/hello" which returns the example
# dictionary as a JSON response with the status code 200 by default.
@app.get("/hello")
async def hello() -> dict[str, str]:
"""Get hello message."""
return {"message": "Hello from FastAPI"}
import uvicorn


# The route can also handle query parameters encoded in the URL after the path,
# e.g. `/random?maximum=1000`
# If the value isn't an integer, FastAPI will return an error response
# with a validation error describing the invalid input.
@app.get("/random")
async def get_random_item(maximum: int) -> dict[str, int]:
"""Get an item with a random ID."""
return {"itemId": random.randint(0, maximum)}
# App + CORS
app = FastAPI(title="Resume Coach API (Gemini)")
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000", "*"], # Change as needed for prod!
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)




# Gemini & Resume Endpoints

'''GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "")
GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.0-flash")

if GEMINI_API_KEY:
genai.configure(api_key=GEMINI_API_KEY)'''






@app.post("/upload")
async def upload_pdf(file: UploadFile = File(...)):
# This function checks and validates the file type
if file.content_type != "application/pdf":
raise HTTPException(status_code=400, detail="Only PDF files are allowed")
try:
contents = await file.read()
# PDF processing function could go here
doc = fitz.open(stream=contents, filetype= "pdf")
text = ""
for page in doc:
text += page.get_text()

#print(text)
job_description = "Software Engineer"
prompt = f"""You are an expert resume reviewer with extensive experience in recruitment and career coaching. You will receive the text content extracted from a resume. Analyze this text and provide detailed, actionable critiques focusing on content quality, language, and structure.
Evaluate the following aspects:
Action Verbs and Language Strength: Assess the use of strong action verbs vs. passive or weak language
Quantifiable Achievements: Identify where metrics and numbers should be added to demonstrate impact
Keyword Optimization: Evaluate the presence of industry-relevant keywords and ATS-friendly terminology
Clarity and Conciseness: Check for verbose descriptions, unclear statements, or redundant information
Professional Tone: Assess grammar, punctuation, spelling, and overall professionalism
Content Structure: Evaluate the logical flow and organization of information
Experience Descriptions: Analyze how effectively accomplishments are communicated
Skills Relevance: Review whether skills listed are specific and marketable
Gaps and Missing Information: Identify where important details or context may be missing
Provide each critique as a separate, specific, actionable suggestion. Format your response as follows:
Each piece of feedback must be on its own line
Separate each feedback item with the pipe symbol "|"
Be specific and actionable in your critiques
Focus only on content, language, and text-based elements
Limit each critique to one clear point
Do not include the pipe symbol at the beginning or end of your response
Example format:
Replace "responsible for" with strong action verbs like "Led," "Developed," or "Implemented"|Add quantifiable metrics to "improved sales" - specify percentage increase or dollar amount|Remove generic phrase "team player" and replace with specific examples of collaboration|Spell out acronyms on first use to ensure ATS compatibility
Now analyze the resume text and provide your critiques. Here is the resume in text form: {text}. This is the job title: {job_description}"""

client = genai.Client()
response = client.models.generate_content(
model = "gemini-2.5-flash", contents = prompt
)

processed_result = {
"message": response.text,
}
return JSONResponse(content=processed_result, status_code=200)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error processing file: {str(e)}")
finally:
await file.close()


if __name__ == "__main__":
uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True)
Binary file added backend/src/sample-resume.pdf
Binary file not shown.
Loading