import streamlit as st import json import tempfile import pandas as pd import altair as alt from typing import Dict, Any, Optional from roe_ai import generate_insights, extract_job_details, evaluate_candidate_job_fit def __init__(): """ Initialize the Streamlit application with configuration and styling. """ _configure_page() _apply_custom_css() def _configure_page(): """ Set up the Streamlit page configuration. """ st.set_page_config( page_title="Resume Intelligence", page_icon="📋", layout="wide", initial_sidebar_state="expanded", ) def _apply_custom_css(): """ Apply custom CSS styling to the Streamlit application. """ st.markdown( """ """, unsafe_allow_html=True, ) def run(): """ Main method to run the Streamlit application. """ _render_title() _configure_sidebar() uploaded_file = _upload_resume() if uploaded_file is not None: _process_resume(uploaded_file) def _render_title(): """ Render the application title and description. """ st.title("📄 Resume Intelligence Platform with Roe AI") st.write("Upload a PDF document to process with our AI agent.") def _configure_sidebar(): """ Configure the sidebar with application settings. """ st.sidebar.header("🔧 Configuration") default_instruction = ( "Please extract actionable insights from the candidates resume" ) st.session_state.insights_prompt = st.sidebar.text_area( "AI Agent Instruction", value=default_instruction, height=150, disabled=True ) st.session_state.page_filter = st.sidebar.text_input( "Page Range to Process", value="@PAGERANGE(1-3)", disabled=True ) def _upload_resume(): """ Handle resume file upload. Returns: Uploaded file or None """ return st.file_uploader( "Choose a PDF file", type="pdf", help="Upload a PDF document to process with the Roe AI agent", ) def _process_resume(uploaded_file): """ Process the uploaded resume and display insights. Args: uploaded_file: Uploaded PDF file """ if st.sidebar.button( "Extract Resume Insights", type="primary", disabled=hasattr(st.session_state, "insights"), ): insights = _extract_resume_insights(uploaded_file) st.session_state.insights = insights if hasattr(st.session_state, "insights"): _display_candidate_profile(st.session_state.insights) _setup_job_description_section(st.session_state.insights) if ( hasattr(st.session_state, "show_scroll_message") and st.session_state.show_scroll_message ): st.sidebar.info("Please scroll down to see results") def _extract_resume_insights(uploaded_file): """ Extract insights from the uploaded resume. Args: uploaded_file: Uploaded PDF file Returns: Extracted insights or None """ with st.spinner("Analyzing resume..."): with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file: temp_file.write(uploaded_file.getvalue()) temp_file_path = temp_file.name result = generate_insights( temp_file_path, st.session_state.insights_prompt, st.session_state.page_filter, ) insights = json.loads(result[0]["value"]) # Validate insights against expected schema required_fields = ["name", "email", "skills"] if all(field in insights for field in required_fields): # st.success("Resume processed successfully!") return insights st.warning("Received response does not match expected schema.") return None def _setup_job_description_section(insights): """ Set up the job description extraction section. Args: insights: Candidate insights dictionary """ st.sidebar.header("🌐 Job Description Extraction") job_url = st.sidebar.text_input( "Job Description URL", placeholder="Enter URL of job description", help="Paste the URL of a job description to extract details", on_change=lambda: None, ) job_details = _extract_job_details(job_url) if job_details: _assess_job_match(job_details, insights) def _extract_job_details(job_url): """ Extract job details from the provided URL. Args: job_url: URL of the job description Returns: Job details dictionary or None """ if job_url: if st.sidebar.button( "Extract Job Details", type="primary", disabled=hasattr(st.session_state, "job_details"), ): st.session_state.show_scroll_message = True with st.spinner("Extracting job description..."): try: result = extract_job_details(job_url) job_details = json.loads(result[0]["value"]) st.session_state.job_details = job_details # st.success("Job description extracted successfully!") except Exception as e: st.error(f"An error occurred: {str(e)}") if hasattr(st.session_state, "job_details"): # Display job details st.subheader("📋 Job Description Details") st.markdown( f"**Job Title:** {st.session_state.job_details.get('job_title', 'N/A')}" ) st.markdown( f"**Location:** {st.session_state.job_details.get('location', 'N/A')}" ) st.markdown( f"**Salary:** {st.session_state.job_details.get('salary', 'N/A')}" ) st.markdown( f"**Equity:** {st.session_state.job_details.get('equity', 'N/A')}" ) st.markdown( f"**Experience Level:** {st.session_state.job_details.get('experience_level', 'N/A')}" ) st.subheader("Key Responsibilities") for responsabilities in st.session_state.job_details.get( "key_responsibilities", [] ): st.markdown(f"- {responsabilities}") st.subheader("Requirements") for requirements in st.session_state.job_details.get("requirements", []): st.markdown(f"- {requirements}") return st.session_state.job_details return None def _assess_job_match(job_details, candidate_insights): """ Assess the job match between candidate insights and job details. Args: job_details: Dictionary of job details candidate_insights: Dictionary of candidate insights """ st.sidebar.header("🔍 Job Match Assessment") if st.sidebar.button("Assess Job Fit", type="primary"): with st.spinner("Analyzing candidate-job match..."): try: # Call job matching assessment function result = evaluate_candidate_job_fit( json.dumps(candidate_insights), json.dumps(job_details) ) if result: match_assessment = json.loads(result[0]["value"]) _render_job_match_assessment(match_assessment) else: st.error("Failed to assess job fit.") except Exception as e: st.error(f"An error occurred during job fit assessment: {str(e)}") def _render_job_match_assessment(match_assessment): """ Render the detailed job match assessment. Args: match_assessment: Dictionary containing job match assessment details """ st.header("📊 Candidate-Job Fit Analysis") # Overall Match Percentage and Recommendation col1, col2, col3 = st.columns(3) with col1: st.metric( label="Overall Match", value=f"{match_assessment.get('percentage_match', 0)}%", help="Percentage of job requirements met by the candidate", ) with col2: _render_recommendation(match_assessment) with col3: st.metric( label="Interview Potential", value=( "Good" if match_assessment.get("percentage_match", 0) > 70 else "Needs Work" ), help="Candidate's potential for interview based on job match", ) # Strengths and Skill Gaps _render_strengths(match_assessment) _render_skill_gaps(match_assessment) # Full Assessment View with st.expander("📄 Detailed Match Assessment"): st.json(match_assessment) def _render_recommendation(match_assessment): """ Render the overall recommendation with color coding. Args: match_assessment: Dictionary containing job match assessment details """ recommendation = match_assessment.get("overall_recommendation", "Unknown") color_map = { "Strong Fit": ("green", "✅"), "Moderate Fit": ("orange", "⚠️"), } color, emoji = color_map.get(recommendation, ("red", "❌")) st.markdown( f"""