import streamlit as st import pandas as pd import plotly.express as px import plotly.graph_objects as go import torch import re import io import docx import json from pypdf import PdfReader from transformers import AutoTokenizer, AutoModelForCausalLM # ─── 1. AI ENGINE SETUP ─────────────────────────────────────────────────── @st.cache_resource def load_ai_model(): MODEL_ID = "Qwen/Qwen2.5-1.5B-Instruct" tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) model = AutoModelForCausalLM.from_pretrained( MODEL_ID, device_map="cpu", torch_dtype=torch.float32, trust_remote_code=True ) return tokenizer, model def ask_ai(system_prompt, user_prompt): tokenizer, model = load_ai_model() messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ] text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) model_inputs = tokenizer([text], return_tensors="pt").to(model.device) with torch.no_grad(): generated_ids = model.generate(**model_inputs, max_new_tokens=512, temperature=0.1) response = tokenizer.decode(generated_ids[0][len(model_inputs.input_ids[0]):], skip_special_tokens=True) return response # ─── 2. FILE PROCESSING ─────────────────────────────────────────────────── def extract_text_from_file(uploaded_file): text = "" try: if uploaded_file.name.endswith('.pdf'): reader = PdfReader(uploaded_file) for page in reader.pages: text += page.extract_text() + "\n" elif uploaded_file.name.endswith('.docx'): doc = docx.Document(io.BytesIO(uploaded_file.read())) text = "\n".join([para.text for para in doc.paragraphs]) except Exception as e: st.error(f"Error reading {uploaded_file.name}: {e}") return text # ─── 3. UI & STYLING ────────────────────────────────────────────────────── st.set_page_config(page_title="Smart ATS AI", page_icon="✨", layout="wide") st.markdown(""" """, unsafe_allow_html=True) if 'current_page' not in st.session_state: st.session_state.current_page = 'landing' if 'results' not in st.session_state: st.session_state.results = None # ─── PAGE 1: LANDING ────────────────────────────────────────────────────── if st.session_state.current_page == 'landing': col1, col2 = st.columns([1.1, 0.9]) with col1: st.markdown('
', unsafe_allow_html=True) st.markdown('

YOUR AI PARTNER FOR SMART HIRING

', unsafe_allow_html=True) if st.button("START AI ANALYSIS"): st.session_state.current_page = 'input' st.rerun() st.markdown('
', unsafe_allow_html=True) with col2: st.image("https://i.postimg.cc/c4nJwQxz/Recruiter-looks-at-a-perfect-candidate-cv-illustration.jpg") # ─── PAGE 2: INPUT ──────────────────────────────────────────────────────── elif st.session_state.current_page == 'input': col_main, _ = st.columns([1.2, 0.8]) with col_main: st.markdown('
', unsafe_allow_html=True) # تصحيح زر الرجوع if st.button("Back to Home"): st.session_state.current_page = 'landing' st.rerun() st.markdown('
', unsafe_allow_html=True) st.markdown('

Recruitment Setup

', unsafe_allow_html=True) j_title = st.text_input("Job Title") j_desc = st.text_area("Job Description", height=150) uploaded_files = st.file_uploader("Upload Resumes", type=["pdf", "docx"], accept_multiple_files=True) if st.button("Analyze Now"): if j_title and j_desc and uploaded_files: final_results = [] with st.spinner("AI Engine is working..."): status_placeholder = st.empty() progress_bar = st.progress(0) for idx, f in enumerate(uploaded_files): status_placeholder.markdown(f'
🔍 Processing: {f.name}
', unsafe_allow_html=True) text_raw = extract_text_from_file(f) sys_p = "Analyze resume against job description. Return JSON only." user_p = f"Job: {j_title}\nResume: {text_raw[:2500]}\nOutput: score, reason, skills, exp." try: ai_raw = ask_ai(sys_p, user_p) match = re.search(r'\{.*\}', ai_raw, re.DOTALL) if match: data = json.loads(match.group()) final_results.append({ "name": f.name.split('.')[0], "score": data.get("score", 0), "skills": data.get("skills", []), "exp": data.get("exp", 0), "status": "Accepted" if data.get("score", 0) >= 70 else "Rejected", "reason": data.get("reason", "N/A") }) except: continue progress_bar.progress((idx + 1) / len(uploaded_files)) st.session_state.results = sorted(final_results, key=lambda x: x['score'], reverse=True) st.session_state.job_title = j_title st.session_state.current_page = 'results' st.rerun() st.markdown('
', unsafe_allow_html=True) # ─── PAGE 3: RESULTS ─────────────────────────────────────────────────────── elif st.session_state.current_page == 'results': st.markdown('
', unsafe_allow_html=True) if st.button("New Analysis"): st.session_state.current_page = 'input' st.rerun() st.title(f"Results for {st.session_state.job_title}") df = pd.DataFrame(st.session_state.results) c1, c2, c3 = st.columns(3) c1.metric("Total", len(df)) c2.metric("Accepted", len(df[df['status']=='Accepted'])) c3.metric("Avg Score", f"{int(df['score'].mean())}%") fig = px.bar(df, x='name', y='score', color='status') st.plotly_chart(fig, use_container_width=True) st.dataframe(df, use_container_width=True) st.markdown('
', unsafe_allow_html=True)