Spaces:
Sleeping
Sleeping
File size: 5,880 Bytes
bee04c6 19ed3f8 640775f a38462e 640775f 19ed3f8 ff091d9 9d91496 640775f ff091d9 640775f 8b3571d 640775f 8c2fc83 8b3571d ff091d9 8b3571d 9d91496 640775f 8c2fc83 640775f 1c0b14d 640775f d942afe 8c2fc83 640775f ff091d9 640775f ff091d9 8c2fc83 1c02d6a 8c2fc83 9d91496 1c02d6a 8c2fc83 640775f 1c02d6a ff091d9 640775f ff091d9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | import os
import streamlit as st
import pandas as pd
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
import torch
from sentence_transformers import SentenceTransformer, util
# --- CONFIGURATION ---
NUM_QUESTIONS_TO_ASK = 5 # <--- NEW: Set the limit here
# --- HF cache setup in a writable folder (/tmp is safe in Docker) ---
HF_CACHE = "/tmp/hf_cache"
os.makedirs(HF_CACHE, exist_ok=True)
os.environ["TRANSFORMERS_CACHE"] = HF_CACHE
os.environ["HF_HOME"] = HF_CACHE
os.environ["HF_DATASETS_CACHE"] = HF_CACHE
os.environ["HF_MODULES_CACHE"] = HF_CACHE
st.set_page_config(page_title="Interviewer Bot", layout="wide")
st.title("π Interviewer Bot")
st.write(f"AI-powered Interviewer Bot for {NUM_QUESTIONS_TO_ASK} random questions.")
# --- Load fine-tuned model (Explicitly passing cache_dir & fixing FutureWarnings) ---
@st.cache_resource(show_spinner=True)
def load_model(model_name, cache_dir):
# FIX: Explicitly setting clean_up_tokenization_spaces to suppress FutureWarning
tokenizer = AutoTokenizer.from_pretrained(
model_name,
cache_dir=cache_dir,
clean_up_tokenization_spaces=True
)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name, cache_dir=cache_dir)
return tokenizer, model
MODEL_NAME = "aswinprasath31/interviewer-bot-finetuned-v1"
tokenizer, model = load_model(MODEL_NAME, HF_CACHE)
# --- Load and preprocess questions from a CSV file (Data Fix for combined Q&A) ---
@st.cache_data
def load_questions(csv_path="training_data.csv"):
df = pd.read_csv(csv_path, header=None)
if df.iloc[0, 0] == 'test':
df = df.iloc[1:].copy()
df[['Question', 'Answer']] = df[0].str.split('Answer:', expand=True)
df['Question'] = df['Question'].str.replace('Question:', '').str.strip()
df['Answer'] = df['Answer'].str.strip()
df.dropna(subset=['Question'], inplace=True)
return df[['Question', 'Answer']].reset_index(drop=True)
# Load the full question set once
full_questions_df = load_questions()
# --- Session state management and Random Sampling (CRITICAL CHANGE) ---
if "sampled_questions" not in st.session_state:
# Randomly sample the specified number of questions (without replacement)
# The 'min' ensures we don't crash if the CSV has fewer than 5 questions
N = min(NUM_QUESTIONS_TO_ASK, len(full_questions_df))
st.session_state.sampled_questions = full_questions_df.sample(n=N).reset_index(drop=True)
if "q_index" not in st.session_state:
st.session_state.q_index = 0
if "responses" not in st.session_state:
st.session_state.responses = []
# Now, all subsequent logic uses the sampled_questions dataframe
questions_df = st.session_state.sampled_questions
num_questions = len(questions_df)
# --- Sentence transformer for similarity scoring (Explicitly passing cache_dir) ---
@st.cache_resource(show_spinner=False)
def get_embedder(cache_folder):
return SentenceTransformer('all-MiniLM-L6-v2', cache_folder=cache_folder)
embedder = get_embedder(HF_CACHE)
def get_reference_answer(question, max_len=200):
inputs = tokenizer(question, return_tensors="pt")
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=max_len)
return tokenizer.decode(outputs[0], skip_special_tokens=True)
def compute_similarity(candidate_answer, reference_answer):
candidate_emb = embedder.encode(candidate_answer, convert_to_tensor=True)
reference_emb = embedder.encode(reference_answer, convert_to_tensor=True)
score = util.cos_sim(candidate_emb, reference_emb).item()
return round(score * 10, 2) # scale 0-10
# --- UI Layout ---
if st.session_state.q_index < num_questions:
st.progress(st.session_state.q_index / num_questions)
# Access the current question from the sampled list
question = questions_df.iloc[st.session_state.q_index]["Question"]
st.subheader(f"Question {st.session_state.q_index + 1}/{num_questions}")
st.write(f"π‘ {question}")
candidate_answer = st.text_area("Your Answer:")
if st.button("Submit Answer"):
if candidate_answer.strip() == "":
st.warning("β οΈ Please enter an answer before submitting.")
else:
# Generate the reference answer using your fine-tuned LLM
reference_answer = get_reference_answer(question)
score = compute_similarity(candidate_answer, reference_answer)
st.success("β
Reference Answer:")
st.write(reference_answer)
st.info(f"π Score (0-10) based on similarity: {score}")
# Save response
st.session_state.responses.append({
"Question": question,
"Candidate Answer": candidate_answer,
"Reference Answer": reference_answer,
"Score": score
})
# Move to the next question
st.session_state.q_index += 1
st.rerun() # Rerun to display the next question or the completion message
else:
# Completion message
st.balloons()
st.success("π Interview completed!")
# Display results and download button
all_responses = pd.DataFrame(st.session_state.responses)
avg_score = all_responses['Score'].mean()
st.markdown(f"### Overall Average Score: **{avg_score:.2f}**")
st.dataframe(all_responses, hide_index=True)
csv = all_responses.to_csv(index=False).encode('utf-8')
st.download_button(
"π₯ Download Responses CSV",
data=csv,
file_name="interview_responses.csv",
type="primary"
)
# Optional: Reset button to start a new random interview
if st.button("Start New Interview"):
del st.session_state.sampled_questions
del st.session_state.q_index
del st.session_state.responses
st.rerun() |