import os import gradio as gr import requests from huggingface_hub import InferenceClient # 🔑 Load API keys from environment GEMINI_API_KEY = os.getenv("GOOGLE_AI_API_KEY") HF_API_KEY = os.getenv("HUGGINGFACE_API_KEY") # optional for private HF models # Hugging Face Client (public model as fallback) hf_client = InferenceClient(model="HuggingFaceH4/zephyr-7b-beta") # ========== AI Functions ========== def generate_with_huggingface(resume_text, job_desc): prompt = f""" You are an expert career assistant. Resume: {resume_text} Job Description: {job_desc} Task: 1. Create a customized resume version highlighting relevant skills and achievements. 2. Write a professional cover letter tailored for this role. """ response = hf_client.text_generation( prompt, max_new_tokens=800, temperature=0.7, ) return response def call_gemini_api(resume_text, job_desc): if not GEMINI_API_KEY: return None prompt = f""" You are an expert career assistant. Resume: {resume_text} Job Description: {job_desc} Task: 1. Create a customized resume version highlighting relevant skills and achievements. 2. Write a professional cover letter tailored for this role. """ url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent" headers = { "Content-Type": "application/json", "X-goog-api-key": GEMINI_API_KEY, } data = {"contents": [{"parts": [{"text": prompt}]}]} try: response = requests.post(url, headers=headers, json=data, timeout=30) result = response.json() if "candidates" in result: return result["candidates"][0]["content"]["parts"][0]["text"] else: return None except Exception: return None def generate_documents(resume_text, job_desc): gemini_output = call_gemini_api(resume_text, job_desc) if gemini_output: output_text = gemini_output source = "✅ Google Gemini" else: output_text = generate_with_huggingface(resume_text, job_desc) source = "⚠️ Gemini failed → Using Hugging Face LLM" # Split Resume + Cover Letter if "Cover Letter" in output_text: parts = output_text.split("Cover Letter") resume_out = parts[0].strip() cover_letter_out = "Cover Letter" + parts[1].strip() else: resume_out = output_text cover_letter_out = "" return resume_out + f"\n\n(Source: {source})", cover_letter_out # ========== Custom CSS ========== custom_css = """ body { background: linear-gradient(-45deg, #ff9a9e, #fad0c4, #a1c4fd, #c2e9fb); background-size: 400% 400%; animation: gradientBG 12s ease infinite; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } @keyframes gradientBG { 0% {background-position: 0% 50%;} 50% {background-position: 100% 50%;} 100% {background-position: 0% 50%;} } .gradio-container { animation: fadeIn 1.2s ease-in-out; } textarea, input { border-radius: 14px !important; box-shadow: 0px 4px 10px rgba(0,0,0,0.2) !important; padding: 10px !important; } button { background: linear-gradient(90deg, #667eea, #764ba2) !important; color: white !important; font-weight: bold !important; border-radius: 25px !important; transition: all 0.3s ease-in-out; padding: 12px 20px !important; } button:hover { transform: scale(1.07); box-shadow: 0px 6px 15px rgba(0,0,0,0.3); } @keyframes fadeIn { from {opacity: 0; transform: translateY(25px);} to {opacity: 1; transform: translateY(0);} } """ # ========== Gradio UI ========== with gr.Blocks(css=custom_css, title="AI Resume & Cover Letter Generator") as demo: gr.Markdown("
Paste your Resume + Job Description → Get a Customized Resume & Cover Letter
") with gr.Row(): resume_input = gr.Textbox(label="📑 Paste Resume / LinkedIn profile", lines=10, placeholder="Paste your resume text here...") job_input = gr.Textbox(label="💼 Paste Job Description", lines=8, placeholder="Paste job description here...") generate_btn = gr.Button("✨ Generate Resume & Cover Letter") with gr.Tabs(): with gr.Tab("🎯 Customized Resume"): resume_output = gr.Textbox(label="Customized Resume", lines=20) with gr.Tab("✍️ Cover Letter"): cover_output = gr.Textbox(label="Cover Letter", lines=20) generate_btn.click(generate_documents, inputs=[resume_input, job_input], outputs=[resume_output, cover_output]) demo.launch()