Jobber/resume-generator/generate_summary.py
2025-12-11 22:04:39 +00:00

138 lines
3.8 KiB
Python

"""
Generate a tailored résumé summary using AI (OpenRouter API).
"""
import os
import json
import requests
import pyperclip
from dotenv import load_dotenv
def load_profile(path: str = "./base.json") -> dict:
"""Load the user's profile from a JSON file."""
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def load_job_description(from_clipboard: bool = True, path: str = None) -> str:
"""
Load the job description from clipboard or a file.
Args:
from_clipboard: If True, read from system clipboard
path: If from_clipboard is False, read from this file path
Returns:
The job description text
"""
if from_clipboard:
return pyperclip.paste().strip()
if path:
with open(path, "r", encoding="utf-8") as f:
return f.read().strip()
raise ValueError("No job description source provided.")
def _build_prompt(profile: dict, jd: str) -> str:
"""Build the prompt for the AI model."""
return f"""
You are generating a tailored résumé summary for me.
Requirements:
- Use keywords found in the job description.
- Keep it concise but meaningful. Avoid fluff. Avoid long-winded text.
- Include just enough detail to feel real and grounded.
- Gently convey that I care about helping people and doing good work.
- Do NOT invent experience or skills I don't have.
- Maintain a warm, confident, human tone.
- Target THIS specific job directly, so use ATS keywords, while remaining natural.
- Use the profile to add context and details.
My profile (JSON fields merged):
{json.dumps(profile, indent=2)}
Job description:
{jd}
Write the résumé summary now.
"""
def _call_openrouter(prompt: str, model: str, api_key: str) -> str:
"""Call OpenRouter API to generate text."""
url = "https://openrouter.ai/api/v1/chat/completions"
headers = {
"Authorization": f"Bearer {api_key}",
"HTTP-Referer": "http://localhost",
"X-Title": "ResumeSummaryScript",
"Content-Type": "application/json",
}
payload = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code != 200:
raise RuntimeError(f"OpenRouter error {response.status_code}: {response.text}")
data = response.json()
return data["choices"][0]["message"]["content"]
def generate_resume_summary(
profile_path: str = "./base.json",
job_description: str = None,
from_clipboard: bool = True,
copy_to_clipboard: bool = True,
) -> str:
"""
Generate a tailored résumé summary using AI.
Uses the user's profile and a job description to generate a personalized
summary section for a résumé, targeting the specific job.
Args:
profile_path: Path to the profile JSON file
job_description: Job description text (if None, uses from_clipboard/path)
from_clipboard: If job_description is None, read JD from clipboard
copy_to_clipboard: If True, copy the generated summary to clipboard
Returns:
The generated résumé summary text
"""
load_dotenv()
api_key = os.getenv("OPENROUTER_API_KEY")
model = os.getenv("MODEL", "openai/gpt-4o-mini")
if not api_key:
raise RuntimeError("Missing OPENROUTER_API_KEY in .env")
profile = load_profile(profile_path)
if job_description is None:
jd = load_job_description(from_clipboard=from_clipboard)
else:
jd = job_description
prompt = _build_prompt(profile, jd)
summary = _call_openrouter(prompt, model, api_key)
if copy_to_clipboard:
pyperclip.copy(summary)
return summary
if __name__ == "__main__":
summary = generate_resume_summary()
print("\n=== Generated Summary ===\n")
print(summary)
print("\n[Summary copied to clipboard]\n")