import os import gradio as gr import numpy as np import faiss from sentence_transformers import SentenceTransformer from groq import Groq # ---------------- CONFIG ---------------- GROQ_API_KEY = os.getenv("GROQ_API_KEY") if not GROQ_API_KEY: raise ValueError("Please set GROQ_API_KEY in Hugging Face Secrets") # ---------------- INIT ---------------- client = Groq(api_key=GROQ_API_KEY) embedder = SentenceTransformer("all-MiniLM-L6-v2") EMBED_DIM = 384 faiss_index = faiss.IndexFlatIP(EMBED_DIM) candidates = [] # ---------------- HELPERS ---------------- def embed_text(text: str): vec = embedder.encode([text]) vec = vec / np.linalg.norm(vec) return vec.astype("float32") def llm_generate(prompt: str): completion = client.chat.completions.create( model="llama-3.1-8b-instant", messages=[{"role": "user", "content": prompt}], temperature=0.3 ) return completion.choices[0].message.content # ---------------- CORE FEATURES ---------------- def add_candidate(name, skills, experience): if not name or not skills: return ( "⚠️ Please fill in required fields.", gr.update(), gr.update(), gr.update() ) profile = f"Name: {name}\nSkills: {skills}\nExperience: {experience}" vec = embed_text(profile) faiss_index.add(vec) candidates.append(profile) return ( f"✅ Candidate **{name}** added successfully.", gr.update(value=""), # clear name gr.update(value=""), # clear skills gr.update(value="") # clear experience ) def generate_bio(raw_data): prompt = f""" You are a professional HR recruiter. Write a concise, formal candidate bio. Rules: - Professional tone - No emojis - No exaggeration - Max 120 words Candidate Data: {raw_data} """ return llm_generate(prompt) def rewrite_job(job_desc): prompt = f""" Rewrite the job description below to meet modern recruitment standards. Make it inclusive, clear, and well-structured. Job Description: {job_desc} """ return llm_generate(prompt) def recommend_candidates(job_query): if not candidates: return "No candidates available.", "", "⚠️ Add candidates first." job_vec = embed_text(job_query) scores, ids = faiss_index.search(job_vec, k=min(3, len(candidates))) ranked, reasons = [], [] for i, idx in enumerate(ids[0]): candidate = candidates[idx] explain_prompt = f""" Explain why this candidate matches the job. Focus only on skills and experience. Candidate: {candidate} Job Requirements: {job_query} """ ranked.append(f"### Rank {i+1}\n{candidate}") reasons.append(llm_generate(explain_prompt)) return "\n\n".join(ranked), "\n\n".join(reasons), "✅ Recommendations generated" # ---------------- LINKEDIN POST GENERATOR (NEW) ---------------- def generate_linkedin_post(context_type, content): prompt = f""" You are a LinkedIn content strategist for HR & AI professionals. Create a LinkedIn post based on the context below. Context Type: {context_type} Rules: - Professional but engaging tone - Short paragraphs - Strong hook at the beginning - Clear CTA at the end - 5–8 relevant hashtags - Max 2 emojis - Do NOT mention AI model names Content: {content} """ return llm_generate(prompt) # ---------------- UI ---------------- theme = gr.themes.Soft( primary_hue="blue", secondary_hue="slate", neutral_hue="gray", ) with gr.Blocks(theme=theme) as demo: gr.Markdown(""" # 🤖 AI Recruitment Assistant **Recruit smarter. Hire faster. Share better.** """) gr.Markdown("---") with gr.Tabs(): # -------- Add Candidate -------- with gr.Tab("➕ Add Candidate"): with gr.Row(): with gr.Column(): name = gr.Textbox(label="Candidate Name *") skills = gr.Textbox(label="Key Skills *") experience = gr.Textbox(label="Experience Summary", lines=4) add_btn = gr.Button("Add Candidate", variant="primary") status = gr.Markdown() with gr.Column(): gr.Markdown(""" ### Tips - Mention years of experience - Use clear skill names - Avoid generic wording """) add_btn.click( add_candidate, inputs=[name, skills, experience], outputs=[status, name, skills, experience] ) # -------- Bio Generator -------- with gr.Tab("🧾 Candidate Bio Generator"): raw_data = gr.Textbox(lines=6, placeholder="Paste raw CV or notes...") bio_btn = gr.Button("Generate Bio", variant="primary") bio_output = gr.Textbox(lines=6) bio_btn.click(generate_bio, raw_data, bio_output) # -------- Job Rewriter -------- with gr.Tab("📄 Job Description Optimizer"): job_desc = gr.Textbox(lines=6, placeholder="Paste job description...") rewrite_btn = gr.Button("Rewrite Professionally", variant="primary") rewrite_output = gr.Textbox(lines=6) rewrite_btn.click(rewrite_job, job_desc, rewrite_output) # -------- Recommendation -------- with gr.Tab("🎯 Candidate Recommendation"): job_query = gr.Textbox(lines=5, placeholder="Enter job requirements...") rec_btn = gr.Button("Find Best Matches", variant="primary") with gr.Row(): rec_candidates = gr.Markdown() rec_explanation = gr.Textbox(lines=10) rec_status = gr.Markdown() rec_btn.click( recommend_candidates, job_query, [rec_candidates, rec_explanation, rec_status] ) # -------- LinkedIn Post Generator (NEW) -------- with gr.Tab("📣 LinkedIn Post Generator"): gr.Markdown("### Turn AI outputs into LinkedIn-ready posts") post_type = gr.Dropdown( label="Post Context", choices=[ "Candidate Bio Highlight", "Job Hiring Announcement", "AI-Based Candidate Recommendation" ] ) post_input = gr.Textbox( lines=8, placeholder="Paste generated bio, job description, or recommendation..." ) post_btn = gr.Button("Generate LinkedIn Post", variant="primary") post_output = gr.Textbox(lines=10, label="LinkedIn Post") post_btn.click( generate_linkedin_post, inputs=[post_type, post_input], outputs=post_output ) # -------- About -------- with gr.Tab("📘 About"): gr.Markdown(""" ### What is this app? An AI-powered recruitment assistant that helps recruiters: - Manage candidates - Improve hiring content - Match talent intelligently - Generate social media content ### Use Case Generate → Optimize → Match → Share on LinkedIn ### Ideal for Recruiters • HR Tech • AI Portfolios • Startups """) gr.Markdown("--- Built for Hugging Face Spaces 🚀") demo.launch()