talent_scout / app.py
lithish2602's picture
Update app.py
3aba6bb verified
import streamlit as st
import json
from mistralai import Mistral
# -----------------------
# Config
# -----------------------
EXIT_KEYWORDS = {"exit", "quit", "bye", "stop", "end"}
STORAGE_FILE = "candidates.json"
# Use Mistral hosted API (instead of local Ollama)
MODEL_NAME = "mistral-small" # can also try "mistral-medium" or "mistral-large"
# Init Mistral LLM
client = Mistral(api_key="XfTNtp3uWrxiDUcEScLRvC86taPOrYln")
# -----------------------
# Helpers
# -----------------------
def save_candidate(data):
"""Save candidate info to local JSON file."""
try:
with open(STORAGE_FILE, "r") as f:
db = json.load(f)
except FileNotFoundError:
db = []
db.append(data)
with open(STORAGE_FILE, "w") as f:
json.dump(db, f, indent=2)
def generate_questions(tech_stack):
"""Generate structured technical questions in ENGLISH using Mistral."""
prompt = f"""
Candidate tech stack: {tech_stack}.
For each technology, generate exactly 3 short interview questions in ENGLISH.
Respond strictly in JSON format like this:
{{
"Python": ["Question1", "Question2", "Question3"],
"Django": ["Question1", "Question2", "Question3"]
}}
No explanations, no extra text, only valid JSON.
"""
response = client.chat.complete(
model=MODEL_NAME,
messages=[{"role": "user", "content": prompt}],
temperature=0.2,
)
raw = response.choices[0].message["content"]
# Try to parse clean JSON
try:
return json.loads(raw)
except:
cleaned = raw.strip().split("```")[-1]
try:
return json.loads(cleaned)
except:
return {"Error": [raw]}
def chat_response(user_input):
"""Handles normal chat responses."""
prompt = f"""
You are TalentScout Hiring Assistant.
Always answer clearly in English.
Do NOT repeat or rephrase the user question, only provide the best possible answer.
Question: {user_input}
Answer:
"""
response = client.chat.complete(
model=MODEL_NAME,
messages=[{"role": "user", "content": prompt}],
temperature=0.2,
)
return response.choices[0].message["content"]
def estimate_skill_levels(qa_history, tech_stack):
"""Estimate skill levels (Beginner / Intermediate / Expert) based on candidate answers."""
answers = "\n".join(
f"Q: {q}\nA: {a}"
for role, msg in qa_history
if role == "You"
for q, a in [(msg, "")]
)
prompt = f"""
Candidate tech stack: {tech_stack}
Candidate interview answers:
{answers}
Based on the answers, estimate the candidate's skill level for each technology
in this stack. Use only these labels: Beginner, Intermediate, Expert.
Respond strictly in JSON format like this:
{{
"Python": "Intermediate",
"Django": "Beginner"
}}
"""
response = client.chat.complete(
model=MODEL_NAME,
messages=[{"role": "user", "content": prompt}],
temperature=0.2,
)
raw = response.choices[0].message["content"]
try:
return json.loads(raw)
except:
return {"Error": raw}
# -----------------------
# Streamlit UI
# -----------------------
st.set_page_config(page_title="TalentScout Hiring Assistant", page_icon="πŸ€–")
st.title("πŸ€– TalentScout Hiring Assistant")
st.write("""
Welcome to the **TalentScout Hiring Assistant**.
I will:
- Collect your professional details (experience, skills, and career interests)
- Generate tailored **technical interview questions** based on your tech stack
- Conduct a structured Q&A session where you can respond to questions or ask clarifications
- Provide a skill-level estimation (Beginner / Intermediate / Expert) for each technology
- Maintain a transcript of the conversation for review
At any time, type **exit**, **quit**, or **end** to finish the interview.
""")
# Session state
if "qa_history" not in st.session_state:
st.session_state.qa_history = []
if "candidate_info" not in st.session_state:
st.session_state.candidate_info = {}
if "chat_input" not in st.session_state:
st.session_state.chat_input = ""
if "skill_levels" not in st.session_state:
st.session_state.skill_levels = {}
# -----------------------
# Candidate Info Form
# -----------------------
with st.form("candidate_form"):
st.subheader("πŸ“‹ Candidate Information")
name = st.text_input("Full Name")
email = st.text_input("Email Address")
phone = st.text_input("Phone Number")
exp = st.number_input("Years of Experience", min_value=0, step=1)
position = st.text_input("Desired Position(s)")
location = st.text_input("Current Location")
tech_stack = st.text_area("Tech Stack (e.g., Python, Django, MySQL)")
submitted = st.form_submit_button("Submit")
if submitted:
st.session_state.candidate_info = {
"name": name,
"email": email,
"phone": phone,
"experience": exp,
"position": position,
"location": location,
"tech_stack": tech_stack,
}
save_candidate(st.session_state.candidate_info)
st.success("βœ… Information saved! Scroll down to see your technical questions.")
# -----------------------
# Generate questions if candidate info available
# -----------------------
if st.session_state.candidate_info.get("tech_stack"):
st.subheader("πŸ§‘β€πŸ’» Technical Questions")
with st.spinner("Generating questions..."):
questions_dict = generate_questions(st.session_state.candidate_info["tech_stack"])
if "Error" in questions_dict:
st.error("⚠️ Could not parse questions properly. Raw output:")
st.write(questions_dict["Error"])
else:
for tech, qs in questions_dict.items():
st.markdown(f"**{tech}**")
for i, q in enumerate(qs, 1):
st.write(f"{i}. {q}")
# -----------------------
# Chatbox for conversation
# -----------------------
st.subheader("πŸ’¬ Chat with Assistant")
def process_input():
"""Handles user input when submitted."""
user_input = st.session_state.chat_input.strip()
if not user_input:
return
cleaned = user_input.lower()
# βœ… Exit keywords
if cleaned in EXIT_KEYWORDS:
st.session_state.qa_history.append(("You", user_input))
st.session_state.qa_history.append(("Assistant", "πŸ‘‹ Thank you for your time! We’ll contact you with next steps."))
st.session_state.chat_input = "" # clears input
return
# Otherwise β†’ normal chat flow
with st.spinner("Assistant is typing..."):
final_response = chat_response(user_input)
st.session_state.qa_history.append(("You", user_input))
st.session_state.qa_history.append(("Assistant", final_response))
# Clear input box after processing
st.session_state.chat_input = ""
# Input box with callback
st.text_input(
"Your message:",
key="chat_input",
on_change=process_input,
)
# Clear conversation button
if st.button("πŸ—‘οΈ Clear Conversation"):
st.session_state.qa_history = []
st.session_state.skill_levels = {}
st.success("Conversation cleared!")
# Display transcript
for role, msg in st.session_state.qa_history:
if role == "You":
st.markdown(f"**πŸ§‘ {role}:** {msg}")
else:
st.markdown(f"**πŸ€– {role}:** {msg}")
# -----------------------
# Skill Level Estimation
# -----------------------
if st.button("πŸ“Š Estimate Skill Levels"):
if st.session_state.qa_history and st.session_state.candidate_info.get("tech_stack"):
with st.spinner("Analyzing candidate answers..."):
st.session_state.skill_levels = estimate_skill_levels(
st.session_state.qa_history,
st.session_state.candidate_info["tech_stack"]
)
st.subheader("πŸ“Œ Skill Level Estimation")
st.json(st.session_state.skill_levels)
else:
st.warning("⚠️ Please complete candidate details and provide some answers first.")