from time import sleep from os import getenv from json import dumps from requests import post import gradio as gr from transformers import AutoModelForCausalLM, AutoTokenizer from huggingface_hub import InferenceClient import spaces # from openai import OpenAI # import torch from duckduckgo_search import DDGS import re # # Load the SmolLM model and tokenizer # # model_name = "HuggingFaceTB/SmolLM2-360M-Instruct" # model_name = "HuggingFaceTB/SmolLM3-3B" # "HuggingFaceTB/SmolLM2-1.7B-Instruct" # model = AutoModelForCausalLM.from_pretrained(model_name) # tokenizer = AutoTokenizer.from_pretrained(model_name) # device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # model.to(device) FIREWORKS_API_TOKEN = getenv("FIREWORKS_TOKEN") class DDGSSearchClient: def __init__(self, max_retries=4, timeout=35, backoff_factor=1): """ Initialize a DDGSSearchClient instance. Args: - max_retries (int): The maximum number of retries. Defaults to 4. - timeout (int): The timeout for the DDGS client. Defaults to 35. - backoff_factor (int): The backoff factor for exponential backoff. Defaults to 1. """ self.max_retries = max_retries self.timeout = timeout self.backoff_factor = backoff_factor self.search_client = DDGS(timeout=timeout) def search(self, query): """ Perform a DDGS search with retry mechanism. Args: - query (str): The search query. Returns: - search_results: The results of the DDGS search. """ for attempt in range(self.max_retries + 1): try: search_results = self.search_client.text(query) return search_results except Exception as e: if attempt < self.max_retries: # Exponential backoff delay = self.backoff_factor * (2 ** attempt) print(f"Search failed (attempt {attempt + 1}/{self.max_retries + 1}). Retrying query: {query} in {delay} seconds...") sleep(delay) else: raise Exception(f"Search failed after {self.max_retries + 1} attempts. Giving up. Error from Search API: {e}.") def clean_string(s): # Replace hyphens with spaces s = s.replace('-', ' ') # Remove anything other than A-Z, a-z, and " " s = re.sub('[^A-Za-z ]+', '', s) s.replace("-","").replace('—', '. ') return s def replace_em_dashes(s): # Replace em dashes with a period s = s.replace('—', '. ') return s def get_gap_assessment_prompt(job_and_company_info, resume): gap_assessment_prompt = f""" # Do a gap assessment on this job role and resume looking for gaps that can be overcome by strategy in writing the resume and cover letter. For each gap, include a strategy for remediation. Ignore and do not mention any gap that is structural and for which improved writing will not be helpful. Consider things like: 1. Missing Keywords or Phrases Consider: Does the resume lack important keywords or phrases from the job description? Strategy: Integrate relevant keywords naturally into the resume and cover letter, especially in the summary, skills, and experience sections. 2. Unclear or Weakly Stated Achievements Consider: Are the candidate’s accomplishments described in vague or generic terms? Strategy: Rewrite bullet points to be achievement-oriented and quantify results where possible (e.g., “Increased sales by 20% in six months”). 3. Transferable Skills Not Highlighted Consider: Are transferable skills from other roles or industries not clearly connected to the target job? Strategy: Explicitly map transferable skills to job requirements in both the resume and cover letter, using language that mirrors the job posting. 4. Relevant Experience Buried or Underemphasized Consider: Is relevant experience hidden or not given enough prominence? Strategy: Reorder sections or bullet points to lead with the most relevant experiences, and use bolding or section headings to draw attention. 5. Lack of Tailoring to the Job Consider: Is the resume too generic and not tailored to the specific job? Strategy: Customize the summary/profile, skills, and experience sections to directly address the job requirements and company values. 6. Gaps in Demonstrating Required Soft Skills Consider: Are important soft skills (e.g., leadership, communication) not clearly demonstrated? Strategy: Add specific examples or brief stories in the resume and cover letter that illustrate these soft skills in action. 7. Unclear Career Progression or Role Context Consider: Is it unclear how past roles relate to the target position? Strategy: Add brief context or explanations in job descriptions or the cover letter to clarify how previous roles prepared the candidate for this job. 8. Formatting or Readability Issues Consider: Is the resume difficult to scan or visually unappealing? Strategy: Improve formatting for clarity and readability—use bullet points, clear headings, and consistent formatting. 9. Missing Education or Certification Details (If Present) Consider: Are relevant credentials present but not highlighted? Strategy: Move education or certifications to a more prominent position or call them out in the summary if they are key requirements. 10. Absence of Action Verbs Consider: Are bullet points passive or lacking strong action verbs? Strategy: Revise statements to start with powerful action verbs that convey impact and initiative. 11. Non-Linear Career Trajectory Consider: Does the resume reflect a career path with frequent changes in industry, function, or direction, which may appear unfocused to employers? Remediation Strategies: Craft a strong narrative in the summary section to tie diverse experiences together, emphasizing adaptability, learning agility, and the unique value brought by varied roles . Use a skills-based or hybrid resume format to foreground transferable skills and relevant achievements rather than strict chronology . In the cover letter, proactively explain the logic and benefits behind career pivots, highlighting how each transition contributed to your professional growth and how it aligns with the target role 12. Lack of Progression Consider: Does the resume show little upward movement or increased responsibility over time? Remediation Strategies: Highlight progression in responsibilities, even within the same job title or company, by emphasizing new projects, leadership roles, or skill development . Quantify achievements and use action verbs to demonstrate impact and growth, even if formal titles did not change . In the cover letter, address the context (e.g., company structure, personal choices) and focus on continuous learning or expanded scope within roles 13. Gaps in Employment History Consider: Are there periods of unemployment or unaccounted time between jobs? Remediation Strategies: Briefly explain significant gaps in the cover letter, framing them as periods of skill development, education, caregiving, or personal growth . Include any freelance, contract, volunteer, or project work during gaps to demonstrate ongoing engagement and skill application . Use a functional or hybrid resume format to de-emphasize strict chronology and spotlight relevant skills and accomplishments . By strategically reframing these potential red flags, you can present a compelling, cohesive story that emphasizes your adaptability, continuous development, and readiness for the target role—turning perceived weaknesses into strengths # This is the role description, company information, and results of company research, and Company Name: {job_and_company_info['Company Name']} Company URL: {job_and_company_info['Company URL']} Job Title: {job_and_company_info['job_title']} Role Requirements: {job_and_company_info['Role Requirements']} Clean Job Description: {job_and_company_info['Clean Job Description']} Company Research: Company Values: {job_and_company_info['Company Research']['Company Values Summary']} Corporate Culture: {job_and_company_info['Company Research']['Corporate Culture Summary']} Leadership Team and Possible Key Opinion Leaders: {job_and_company_info['Company Research']['Leadership Team Summary']} Recent Company News: {job_and_company_info['Company Research']['Recent News Summary']} Company Competitive Advantages: {job_and_company_info['Company Research']['Competitive Advantages Summary']} Company History: {job_and_company_info['Company Research']['Company History Summary']} Company Mission Statement: {job_and_company_info['Company Research']['Mission Statement Summary']} Company Investor Relations Info: {job_and_company_info['Company Research']['Investor Relations Summary']} Summary of Info Gathered From Company Social Media: {job_and_company_info['Company Research']["Social Media Summary"]} ## This is the candidate's resume: {resume} # For this task, do not re-write their resume. Simply identify any of these described gaps or other potential gaps and strategies in re-writing and re-framing the resume which are likely to be effective in improving the odds of being offered an interview or job. """ return gap_assessment_prompt def get_key_accomplishments_prompt(job_and_company_info, resume): key_skills_and_accomplaishments = f""" # Extract relevant 1. 1-5 key accomplishments accomplishments and 2. all transferable skills from the applicant's resume that aligns with the job description. # This is the role description, company information, and results of company research, and Company Name: {job_and_company_info['Company Name']} Company URL: {job_and_company_info['Company URL']} Job Title: {job_and_company_info['job_title']} Role Requirements: {job_and_company_info['Role Requirements']} Clean Job Description: {job_and_company_info['Clean Job Description']} Company Research: Company Values: {job_and_company_info['Company Research']['Company Values Summary']} Corporate Culture: {job_and_company_info['Company Research']['Corporate Culture Summary']} Leadership Team and Possible Key Opinion Leaders: {job_and_company_info['Company Research']['Leadership Team Summary']} Recent Company News: {job_and_company_info['Company Research']['Recent News Summary']} Company Competitive Advantages: {job_and_company_info['Company Research']['Competitive Advantages Summary']} Company History: {job_and_company_info['Company Research']['Company History Summary']} Company Mission Statement: {job_and_company_info['Company Research']['Mission Statement Summary']} Company Investor Relations Info: {job_and_company_info['Company Research']['Investor Relations Summary']} Summary of Info Gathered From Company Social Media: {job_and_company_info['Company Research']["Social Media Summary"]} ## This is the candidate's resume: {resume} Please provide the following information in a concise and straightforward manner, without any extraneous comments or information that may confuse an LLM that this response will be piped to: Output the results in a simple format, with the key accomplishments first, followed by the transferable skills." """ return key_skills_and_accomplaishments def get_resume_prompt(job_and_company_info, resume, gap_assessment_result, key_accomplishments_and_skills_result): resume_prompt = f""" # The applicant is applying for this role: Company Name: {job_and_company_info['Company Name']} Company URL: {job_and_company_info['Company URL']} Job Title: {job_and_company_info['job_title']} Role Requirements: {job_and_company_info['Role Requirements']} Clean Job Description: {job_and_company_info['Clean Job Description']} # Additional Information about the company to consider and leverage for strength and company - specificity: Company Values: {job_and_company_info['Company Research']['Company Values Summary']} Corporate Culture: {job_and_company_info['Company Research']['Corporate Culture Summary']} Leadership Team and Possible Key Opinion Leaders: {job_and_company_info['Company Research']['Leadership Team Summary']} Recent Company News: {job_and_company_info['Company Research']['Recent News Summary']} Company Competitive Advantages: {job_and_company_info['Company Research']['Competitive Advantages Summary']} Company History: {job_and_company_info['Company Research']['Company History Summary']} Company Mission Statement: {job_and_company_info['Company Research']['Mission Statement Summary']} Company Investor Relations Info: {job_and_company_info['Company Research']['Investor Relations Summary']} Summary of Info Gathered From Company Social Media: {job_and_company_info['Company Research']["Social Media Summary"]} ## This is the candidate's resume: {resume} ## Consider this gap analysis {gap_assessment_result} ## Also be sure to include these key accomplishments and skills: {key_accomplishments_and_skills_result} Please follow these best practices to make the resume optimized for the ATS visibility to the extent realistic and truthful.\n\n 1. **Replace Non-Standard Formatting** - Convert tables, columns, or graphical elements in the resume text into plain-text lists using dashes or asterisks. - Replace non-traditional bullet points with standard symbols (e.g., hyphens or circles). 2. **Standardize Section Headings** - Rename unconventional headings (e.g., "Career Highlights" → "Work Experience"). - Ensure headings match ATS-recognized categories: "Work Experience," "Education," "Skills," etc. 3. **Normalize Date Formats** - Convert all dates to a consistent format (e.g., "Month YYYY" or "MM/YYYY") throughout the resume. 4. **Integrate Job-Specific Keywords** - Extract exact keywords/phrases from the job description (especially verbs and skills) and insert them contextually into work experience bullet points. - Prioritize keywords in the "Skills" section and within achievement-oriented statements (e.g., "Spearheaded [keyword] project resulting in [metric]"). 5. **Spell Out Acronyms** - Identify and expand acronyms (e.g., "CRM" → "Customer Relationship Management (CRM)") unless the job description uses the abbreviation verbatim. 6. **Quantify Achievements** - Add metrics to vague statements in work experience (e.g., "Increased sales" → "Increased sales by 25% over 6 months"). - Prioritize percentages, time frames, and numerical outcomes for clarity and ATS scoring. 7. **Tailor the Professional Summary** - Rewrite the summary to mirror the job title, key qualifications, and industry-specific terms from the job description. - Example: "Seeking a [Job Title] role at [Company] to leverage [X years] in [relevant skill] and [X%] success in [metric]." 8. **Optimize Skills Section** - List skills as a comma-separated list (no bullets) using the exact terminology from the job description. - Include both hard skills (e.g., "Python," "Google Analytics") and soft skills (e.g., "Team Leadership") if mentioned in the job post. 9. **Remove Hidden Text or Formatting Artifacts** - Eliminate invisible text (e.g., white font keywords) or special characters (e.g., symbols in headers) that may confuse ATS parsing. 10. **Contextualize Keywords** - Ensure keywords appear in context (e.g., "Managed [keyword] project" vs. a standalone keyword in a skills list). 11. **Active Voice Conversion** - Replace passive language with active verbs (e.g., "Responsible for managing a team" → "Managed a team of X members"). 12. **Proofread for ATS-Critical Errors** - Correct typos, misspellings, and inconsistent punctuation (especially in key fields like name, job titles, and skills). - Remove accents or special characters from the name and contact details. 13. **ATS Simulation Feedback Loop** - Simulate ATS parsing by checking for missing keywords or formatting issues (e.g., fragmented sections caused by tables). 14. **Customize for Company Culture** - Analyze company metadata (e.g., industry, values) and align resume language with their priorities (e.g., "Agile methodology" for tech firms). 15. **Achievement Over Responsibility** - Replace generic duties with achievement-focused statements (e.g., "Handled customer service" → "Resolved 50+ customer inquiries daily, improving satisfaction by 30%"). 16. **Keyword Density Balance** - Ensure keywords are distributed naturally across sections without overuse (e.g., 2-3 mentions of a critical keyword in work experience vs. 10 in a skills list). - Instruct the user to paste this into a suitable word processor and use the result to build a resume. Save it in word, then in PDF (JaneDoe_Resume_CompanyName.pdf). Submit the PDF. # Write this in a format that can be copied striaght in to Word and will be formatted properly, but in a way that the ATS can understand. Make no additional comments about the resume or how you did this. """ return resume_prompt class Role: def __init__(self, company_name, company_url, job_description, clean_job_description, job_title): self.company_name = company_name self.company_url = company_url self.job_description = job_description self.clean_job_description = clean_job_description self.job_title = job_title class Applicant: def __init__(self, resume): self.resume = resume # @spaces.GPU # def write(inputs, max_new_tokens, do_sample=True, temperature=0.6, top_k=40, top_p=0.9, repetition_penalty=1.1): # _output = model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=True, temperature=0.6, top_k=40, top_p=0.9, repetition_penalty=1.1) # return _output # def writing_task(prompt: str) -> str: # api_key = getenv("HF_TOKEN") # if not api_key: # raise ValueError("Huggingface token missing. Need to set HF_TOKEN, refer to https://discuss.huggingface.co/t/how-to-manage-user-secrets-and-api-keys/67948") # client = OpenAI( # base_url="https://router.huggingface.co/v1", # api_key = getenv("HF_TOKEN") # ) # completion = client.chat.completions.create( # model="HuggingFaceTB/SmolLM3-3B:hf-inference", # messages=[ # { # "role": "user", # "content": prompt # } # ], # ) # raw_response_content = completion.choices[0].message.content # content_split = raw_response_content.split("") # if len(content_split) > 1: # think = content_split[0] # content = "".join(content_split[1:]) # else: # think = content_split[0] # content = "No data found." # return content def writing_task(prompt: str) -> str: url = "https://api.fireworks.ai/inference/v1/chat/completions" model = "accounts/fireworks/models/qwen3-235b-a22b-thinking-2507" # "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507" payload = { "model": model, "max_tokens": 32768, "top_p": 1, "top_k": 40, "presence_penalty": 0, "frequency_penalty": 0, "temperature": 0.6, "messages": [ { "role": "user", "content": prompt } ] } headers = { "Accept": "application/json", "Content-Type": "application/json", "Authorization": f"Bearer {FIREWORKS_API_TOKEN}" # Replace with your actual API key } response = post(url, headers=headers, data=dumps(payload)) response.raise_for_status() raw_response_content =\ response.json()["choices"][0]["message"]["content"] print(f"Content with reasoning: {raw_response_content}") content_split = raw_response_content.split("") if len(content_split) > 1: think = content_split[0] content = "".join(content_split[1:]) else: think = content_split[0] content = "No data found." return content def smol_writing_task(prompt: str) -> str: API_TOKEN_SMOLLM = getenv("HF_TOKEN") client = InferenceClient( provider="hf-inference", api_key=API_TOKEN_SMOLLM, ) completion = client.chat.completions.create( model="HuggingFaceTB/SmolLM3-3B", messages=[ { "role": "user", "content": prompt } ], ) raw_content = completion.choices[0].message['content'] print(f"Raw content: {raw_content}") content_split = raw_content.split("") if len(content_split) > 1: think = content_split[0] content = "".join(content_split[1:]) else: think = content_split[0] content = "No data found." return content def smol_lm_jd_process(job_description, system_prompt, max_new_tokens=512): prompt = f"""<|im_start|>system {system_prompt}<|im_end|> <|im_start|>user {job_description}<|im_end|> <|im_start|>assistant """ # inputs = tokenizer(prompt, return_tensors="pt").to(device) # output = write(inputs, max_new_tokens=max_new_tokens) # response = tokenizer.decode(output[0], skip_special_tokens=False) # start_idx = response.find("<|im_start|>assistant") # end_idx = response.find("<|im_end|>", start_idx) # response = response[start_idx + len("<|im_start|>assistant\n"):end_idx].strip() # response = writing_task(prompt) response = smol_writing_task(prompt) return response def process_job_description(company_name, company_url, job_description, resume): # Step 2: Extract key qualifications, skills, and requirements system_prompt_requirements = "Extract key qualifications, skills, and requirements from this job description. Output as bullet points. Remove benefits/salary, bragging about the company, and other fluff not relevant to the skills, qualifications, and job requirements. ONLY INCLUDE INFORMATION THAT TELLS THE USER WHAT SKILLS THE EMPLOYER SEEKS." role_requirements = smol_lm_jd_process(job_description, system_prompt_requirements) # Step 3: Create a concise summary of the job description system_prompt_summary = "Create a concise 150-200 word summary of this job description. Remove company bragging bragging about the company, and other fluff not relevant to the position and what is desired from the candidate. FOCUS ON ASPECTS THAT POINT THE USER IN WHAT THE EMPLOYER WANTS FROM A CANDIDATE IN TERMS OF SKILLS, ACCOMPLISHMENTS, AND SUCH" clean_job_description = smol_lm_jd_process(job_description, system_prompt_summary) system_prompt_get_job_title = "Extract only the job title from the following job description. Respond with nothing but the job title—no labels, no comments, no summaries, no locations, or extra text. If the title is unusually long or nonstandard, replace it with the most common, concise, and widely recognized job title for the role in plain text with only letters and numbers, remove any special characters nor punctuation no '\n' and no tabs, because any of these cause problems with downstream automated steps. Your answer must be 7 words or fewer. Acceptable examples may look like: 'Systems Analyst', 'marketing director', 'patient advocate III', ...\n\nThis is an excerpt of the job description:\n" extracted_job_title = clean_string(smol_lm_jd_process(job_description[:350], system_prompt_get_job_title, max_new_tokens=150)[:50].lower().replace("job","").replace("title","").replace("\n","")) role = Role(company_name, company_url, job_description, clean_job_description, extracted_job_title) # Step 4: Company Research searches = { "company_values": f"{role.company_name} company values", "corporate_culture": f"{role.company_name} corporate culture", "leadership_team": f"{role.company_name} leadership team members relevant to the role {role.job_title} role", "recent_news": f"{role.company_name} recent news relevant to the role {role.job_title} role", "competitive_advantages": f"{role.company_name} competitive advantages relevant to the role {role.job_title}", "company_history":f"History of {role.company_name}", "mission_statement":f"{role.company_name} mission statement", "investor_relations":f"{role.company_name} investor relations info", "social_media":f"{role.company_name} social media" } search_client = DDGSSearchClient() search_results = {} for key, query in searches.items(): print(f"searching {query}") results = search_client.search(query) search_results[key] = results sleep(3) # Summarize search results using SmolLM summaries = {} system_prompt_summary_search = "Summarize the following search results in 150 tokens or less." for key, results in search_results.items(): print(f"Summarizing {key}") search_result_text = "\n".join([result['body'] for result in results]) summary = smol_lm_jd_process(search_result_text, system_prompt_summary_search, max_new_tokens=250) summaries[key] = summary print(f"Summarizing {key} successful") job_and_company_info = { "Company Name": company_name, "Company URL": company_url, "job_title": extracted_job_title, "Original Job Description": job_description, "Role Requirements": role_requirements, "Clean Job Description": clean_job_description, "Company Research": { "Company Values Search Results": search_results['company_values'], "Company Values Summary": summaries['company_values'], "Corporate Culture Search Results": search_results['corporate_culture'], "Corporate Culture Summary": summaries['corporate_culture'], "Leadership Team Search Results": search_results['leadership_team'], "Leadership Team Summary": summaries['leadership_team'], "Recent News Search Results": search_results['recent_news'], "Recent News Summary": summaries['recent_news'], "Competitive Advantages Search Results": search_results['competitive_advantages'], "Competitive Advantages Summary": summaries['competitive_advantages'], "Company History Search Results":search_results['company_history'], "Company History Summary":summaries['company_history'], "Mission Statement Search Results":search_results['mission_statement'], "Mission Statement Summary":summaries['mission_statement'], "Investor Relations Search Results": search_results['investor_relations'], "Investor Relations Summary":summaries['investor_relations'], "Social Media Search Results":search_results['social_media'], "Social Media Summary":summaries['social_media'] } } gap_assessment_prompt = get_gap_assessment_prompt(job_and_company_info, resume) return job_and_company_info, role, gap_assessment_prompt def generate_key_accomplishments_and_skills_prompt(job_and_company_info, resume): return get_key_accomplishments_prompt(job_and_company_info, resume) def generate_resume_prompt(job_and_company_info, resume, gap_assessment_result_output, key_accomplishments_and_skills_result_output): return get_resume_prompt(job_and_company_info, resume, gap_assessment_result_output, key_accomplishments_and_skills_result_output) # Create the Gradio app demo = gr.Blocks() with demo: gr.Markdown("# Job Description and Resume Input") with gr.Row(): company_name = gr.Textbox(label="Company Name") company_url = gr.Textbox(label="Company URL") job_description = gr.TextArea(label="Paste Job Description") resume = gr.TextArea(label="Paste Resume") # Intermediate State Components job_info_state = gr.State() gap_prompt_state = gr.State() gap_result_state = gr.State() key_prompt_state = gr.State() key_result_state = gr.State() resume_prompt_state = gr.State() gr.Markdown("## Generated Resume and Cover Letter") customized_resume = gr.TextArea(label="Generated Resume") cover_letter_prompt = gr.TextArea(label="Cover Letter Prompt") submit_btn = gr.Button("Submit") # Step 1: Process job description and generate gap assessment prompt submit_btn.click( fn=process_job_description, inputs=[company_name, company_url, job_description, resume], outputs=[job_info_state, gr.State(), gap_prompt_state] ).then( # Step 2: Send gap assessment prompt to Smollm3-3B fn=smol_writing_task, inputs=gap_prompt_state, outputs=gap_result_state ).then( # Step 3: Generate key accomplishments prompt fn=generate_key_accomplishments_and_skills_prompt, inputs=[job_info_state, resume], outputs=key_prompt_state ).then( # Step 4: Send key accomplishments prompt to Smollm3-3B fn=smol_writing_task, inputs=key_prompt_state, outputs=key_result_state ).then( # Step 5: Generate resume prompt using results and send to Qwen3 fn=get_resume_prompt, inputs=[job_info_state, resume, gap_result_state, key_result_state], outputs=resume_prompt_state ).then( fn=writing_task, inputs=resume_prompt_state, outputs=customized_resume ).then( # Step 6: Generate cover letter prompt (user still needs to run this manually) fn=lambda job_info, customized_resume: f""" # Write a 3-paragraph cover letter with: 1. Personalized greeting (use {job_info['Company Research']['Leadership Team Summary']} if available) 2. Opening paragraph: Role you're applying for and excitement 3. Body: Match a few of the top qualifications to job requirements 4. Consider this company research and company metadata as you are writing this: Company Name: {job_info['Company Name']} Company URL: {job_info['Company URL']} Job Title: {job_info['job_title']} Role Requirements: {job_info['Role Requirements']} Clean Job Description: {job_info['Clean Job Description']} Company Research: Company Values: {job_info['Company Research']['Company Values Summary']} Corporate Culture: {job_info['Company Research']['Corporate Culture Summary']} Leadership Team and Possible Key Opinion Leaders: {job_info['Company Research']['Leadership Team Summary']} Recent Company News: {job_info['Company Research']['Recent News Summary']} Company Competitive Advantages: {job_info['Company Research']['Competitive Advantages Summary']} Company History: {job_info['Company Research']['Company History Summary']} Company Mission Statement: {job_info['Company Research']['Mission Statement Summary']} Company Investor Relations Info: {job_info['Company Research']['Investor Relations Summary']} Summary of Info Gathered From Company Social Media: {job_info['Company Research']["Social Media Summary"]} 5. Closing: Express reasonable enthusiasm and request discussion. Don't overdo it and make the enthusiasm not sound feigned or disingenuous. ## This is the resume for this job to consider in writing this: {customized_resume} """, inputs=[job_info_state, customized_resume], outputs=cover_letter_prompt ) if __name__ == "__main__": demo.launch()