resume-reviser / app.py
ahuang11's picture
Update app.py
066b023 verified
raw
history blame contribute delete
No virus
8.27 kB
import os
import operator
from typing import TypedDict, Annotated, Sequence
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.messages import BaseMessage, SystemMessage
from langgraph.graph import StateGraph, END
import panel as pn
pn.extension()
os.environ["LANGCHAIN_TRACING_V2"] = "true"
JOB_DESCRIPTION = """
Quansight has its roots in the Python data science community. Our founders have had significant involvement in creating and maintaining NumPy, SciPy, Jupyter, Spyder, Dask, Conda, Numba, and other projects, as well as PyData NumFOCUS, and Anaconda. Our mission is to connect companies to open-source communities to create sustainable solutions that benefit the whole ecosystem.
We accomplish this mission by providing various services ranging from open-source software development to training and consulting. We believe in a culture of do-ers, learners, and collaborators. We are looking for people who are motivated, humble, curious, and respectful of others.
We are looking for enthusiastic and highly motivated software developers with extensive experience in the Scientific Python and PyData ecosystems to help support Quansight’s growing consulting business. This is an excellent opportunity for you to apply your software development skills to the open-source, Python, data-science, AI, big data and visualization ecosystems, and to help customers apply these tools to solve practical business problems.
What you’ll do
As a Senior PyData Software Engineer, you will be a key member of our team solving problems for our clients using tools from the Python open-source ecosystem. As appropriate, you will help maintain and push developments back upstream to open source projects. We are especially interested in people with a strong technical background who have experience or have an interest in becoming technical leads. Our client projects vary widely across business domains and technologies, being comfortable with growing and learning new technologies will be important to fitting in at Quansight. Key areas we touch on when building solutions for clients include algorithm development, data engineering, data science, machine learning/AI, visualization, packaging, infrastructure and integration.
Requirements:
Fluency in Python
Extensive experience with the Scientific Python and Data Science ecosystems, i.e. Pandas, NumPy, Scikit-Learn, etc.
Experience applying the PyData stack to data and scientific projects
Familiarity with software engineering best practices, including unit tests, code review, version control, CI/CD, etc.
Superior written and verbal communication skills, including writing and reviewing documentation
Ability to make technical and architectural decisions with minimal oversight.
Additionally one or more of the following skills will help you stand out:
Contributions to open source projects
Skills in other programming languages
Experience with Generative AI / LLM's
Experience with Visualization tools like Panel, Streamlit, Dash etc.
Experience with DevOps and Infrastructure-as-Code approaches
Experience with Python packaging and Conda environments
Experience with advanced Python data libraries such as Dask and Numba
Experience working in a client-facing environment
Exposure to building data engineering pipelines using Prefect, Airflow and similar tools
Practical Experience in MLOps approaches
Ability to provide technical leadership to others
Why should you join?
You'll become essential to a small, collaborative, fully distributed accessibility and engineering team. We strive to provide a working environment that gives you room to learn and grow.
""".strip()
RESUME_BULLETS = """
o Investigated the cause of anomalous sea surface temperatures following tropical cyclones (TCs) in the Eastern Tropical Pacific
o Deduced, quantitatively, that TCs tend to suppress low clouds there, enhancing shortwave fluxes, inducing a warming anomaly
o Demonstrated that long-short-term-memory models improved predictions at long leads but not at short leads
""".strip()
AVATARS = {
"critique": "🔍",
"revise": "📝",
}
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
original_bullets: str
job_description: str
keywords_count: int
async def revise(state: AgentState):
instructions = """
Understand the critique first and identify what keywords from the critique
needs to be bolded. Then apply the actionable steps to revise the resume bullets.
Revisions should be similar length as the original bullets.
Ensure the keywords from the critique are not awkwardly
placed, not deceitful, and the bullets are coherent and in proper sentence casing.
"""
messages = [SystemMessage(instructions)] + state["messages"][-2:]
response = await model.ainvoke(messages)
return {"messages": [response]}
async def critique(state: AgentState):
job_description = state["job_description"]
keywords_slider = state["keywords_count"]
instructions = f"""
You are a resume reviewer, optimizing for ATS.
Review the job description and first list out
at least `{keywords_slider}` unique, critical
ATS keywords from the `Job Description`.
If previously `Critique`d, verify if the changes were accurately made;
if not, rephrase original critique to be more actionable.
Determine whether the resume bullets have at least
{keywords_slider} keywords from the `Job Description` in bold.
If not, provide actionable steps to revise (but do not do the revision),
e.g. explicitly telling what keywords to bold from `Job Description` or
what to rephrase to include reflecting the `Job Description`.
If no changes are needed, simply output "Looks good!";
else output the critique starting with `Critique`: and keywords.
Here's the `Job Description`:
'''
{job_description}
'''
"""
messages = [SystemMessage(instructions)] + state["messages"][-2:]
response = await model.ainvoke(messages)
return {"messages": [response]}
def continue_revising(state: AgentState):
last_message = state["messages"][-1].content
return "looks good" not in last_message.lower()
async def respond(content: str, user: str, instance: pn.chat.ChatInterface):
if not job_input.value:
instance.stream(
user="Exception", value="Please provide a job description.", respond=False
)
return
response = app.astream(
{
"original_bullets": resume_input.value,
"messages": [content],
"job_description": job_input.value,
"keywords_count": keywords_slider.value,
}
)
async for chunk in response:
for user, output in chunk.items():
message = output["messages"][-1].content
if user != "__end__":
avatar = AVATARS.get(user, "🤖")
instance.stream(user=user.title(), value=message, avatar=avatar)
model = ChatOpenAI()
# add components
workflow = StateGraph(AgentState)
workflow.add_node("critique", critique)
workflow.add_node("revise", revise)
# add connections
workflow.set_entry_point("critique")
workflow.add_edge("revise", "critique")
workflow.add_conditional_edges(
"critique", continue_revising, {True: "revise", False: END}
)
app = workflow.compile()
# create UI
keywords_slider = pn.widgets.IntSlider(
start=1,
end=10,
value=3,
name="Keywords to Match",
sizing_mode="stretch_width",
)
job_input = pn.widgets.TextAreaInput(
value=JOB_DESCRIPTION,
name="Job Description",
resizable="height",
auto_grow=True,
sizing_mode="stretch_width",
)
resume_input = pn.widgets.TextAreaInput(
placeholder="Paste in the bullets you want revised.",
resizable="height",
rows=4,
value=RESUME_BULLETS,
)
interface = pn.chat.ChatInterface(
callback=respond,
widgets=[resume_input],
show_undo=False,
show_button_name=False,
)
template = pn.template.FastListTemplate(
main=[interface],
sidebar=[keywords_slider, job_input],
sidebar_width=500,
title="Resume Reviser",
)
template.servable()