import os import gradio as gr import requests from huggingface_hub import InferenceClient # Load Gemini API key from environment variable GEMINI_API_KEY = os.getenv("GOOGLE_AI_API_KEY") HF_API_KEY = os.getenv("HUGGINGFACE_API_KEY") # optional if private model # Hugging Face Client (free model, no API key needed for public models) hf_client = InferenceClient(model="HuggingFaceH4/zephyr-7b-beta") 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 # force fallback 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: output_text = result["candidates"][0]["content"]["parts"][0]["text"] return output_text else: return None except Exception: return None def generate_documents(resume_text, job_desc): # Try Gemini first 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 # Gradio UI with gr.Blocks(title="AI Resume & Cover Letter Generator") as demo: gr.Markdown("# 📄 AI Resume & Cover Letter Generator") gr.Markdown("Upload your resume or paste LinkedIn profile + Job description, and get customized documents.\ \n(Default: Gemini, fallback: Hugging Face LLM)") 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.Row(): resume_output = gr.Textbox(label="Customized Resume", lines=15) cover_output = gr.Textbox(label="Cover Letter", lines=15) generate_btn.click(generate_documents, inputs=[resume_input, job_input], outputs=[resume_output, cover_output]) demo.launch()