File size: 10,902 Bytes
555d77a
d4c83d2
555d77a
9d504d6
 
 
a2c958a
9d504d6
2217a1e
555d77a
d4c83d2
 
555d77a
9d504d6
 
 
 
 
da578f7
 
9d504d6
 
 
 
 
 
ba65c08
c050106
 
 
555d77a
d0dc9ad
 
65d1540
1535def
9d504d6
777cd06
555d77a
c050106
 
da578f7
c6fab7c
da578f7
777cd06
c6fab7c
07b37ec
2217a1e
 
 
 
 
 
 
 
 
da578f7
75a27fc
b0ff7a0
da578f7
 
c6fab7c
da578f7
c6fab7c
 
 
 
 
 
 
 
 
 
 
da578f7
 
c6fab7c
da578f7
b0ff7a0
c6fab7c
da578f7
75a27fc
 
c6fab7c
da578f7
 
 
 
c6fab7c
da578f7
 
2217a1e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66d9c35
 
 
c6fab7c
 
2f09479
1535def
555d77a
1535def
c6fab7c
da578f7
 
d0dc9ad
da578f7
 
1535def
9d504d6
da578f7
9d504d6
2217a1e
9d504d6
 
 
 
 
2217a1e
 
 
 
 
9d504d6
 
bd063e2
923245a
c6fab7c
65d1540
da578f7
c6fab7c
2217a1e
 
 
 
 
 
c6fab7c
da578f7
 
c6fab7c
65d1540
923245a
 
1535def
c6fab7c
da578f7
 
65d1540
 
 
 
da578f7
 
bd063e2
 
 
 
 
 
 
 
65d1540
af2de81
bd063e2
 
65d1540
 
 
c6fab7c
da578f7
c6fab7c
da578f7
c6fab7c
 
66d9c35
3d267b0
 
 
 
 
 
65d1540
 
2217a1e
 
 
 
 
 
 
65d1540
 
2217a1e
65d1540
2217a1e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65d1540
2217a1e
65d1540
 
2217a1e
 
 
 
 
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import streamlit as st
from openai import OpenAI
import os
import pandas as pd
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import torch
import re

# Set up OpenAI client
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# Check if GPU is available
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using device: {device}")

# Load metadata and embeddings (ensure these files are in your working directory or update paths)
metadata_path = 'question_metadata.csv'  # Update this path if needed
embeddings_path = 'question_dataset_embeddings.npy'  # Update this path if needed

metadata = pd.read_csv(metadata_path)
embeddings = np.load(embeddings_path)

# Load the SentenceTransformer model
model = SentenceTransformer("all-MiniLM-L6-v2").to(device)

# Load prompts from files
with open("technical_interviewer_prompt.txt", "r") as file:
    technical_interviewer_prompt = file.read()

with open("question_generation_prompt.txt", "r") as file:
    question_generation_prompt = file.read()
    
st.title("Real-World Programming Question Mock Interview")

# Initialize session state variables
if "messages" not in st.session_state:
    st.session_state.messages = []

if "follow_up_mode" not in st.session_state:
    st.session_state.follow_up_mode = False  # Tracks whether we're in follow-up mode

if "generated_question" not in st.session_state:
    st.session_state.generated_question = None  # Stores the generated question for persistence

if "code_template" not in st.session_state:
    st.session_state.code_template = ""  # Stores the code template

if "sample_test_case" not in st.session_state:
    st.session_state.sample_test_case = ""  # Stores the sample test case

if "expected_output" not in st.session_state:
    st.session_state.expected_output = ""  # Stores the expected output

if "debug_logs" not in st.session_state:
    st.session_state.debug_logs = None  # Stores debug logs for toggling

# Function to find the top 1 most similar question based on user input
def find_top_question(query):
    # Generate embedding for the query
    query_embedding = model.encode(query, convert_to_tensor=True, device=device).cpu().numpy()
    
    # Reshape query_embedding to ensure it is a 2D array
    query_embedding = query_embedding.reshape(1, -1)  # Reshape to (1, n_features)

    # Compute cosine similarity between query embedding and dataset embeddings
    similarities = cosine_similarity(query_embedding, embeddings).flatten()  # Flatten to get a 1D array of similarities

    # Get the index of the most similar result (top 1)
    top_index = similarities.argsort()[-1]  # Index of highest similarity

    # Retrieve metadata for the top result
    top_result = metadata.iloc[top_index].copy()
    top_result['similarity_score'] = similarities[top_index]

    return top_result

# Function to generate response using OpenAI API with debugging logs
def generate_response(messages):
    # For debug logs, store only the follow-up conversation history
    st.session_state.debug_logs = st.session_state.messages  # Update debug logs with current conversation
    
    response = client.chat.completions.create(
        model="o1-mini",
        messages=messages,
    )
    
    return response.choices[0].message.content

# Function to extract code template and sample test case from the generated question
def extract_code_and_test_case(generated_question):
    code_template = ""
    sample_test_case = ""
    expected_output = ""
    
    # Extract code template
    code_match = re.search(r'```python(.*?)```', generated_question, re.DOTALL)
    if code_match:
        code_template = code_match.group(1).strip()
    else:
        # Default code template if none is found
        code_template = "# Write your code here\n"

    # Extract sample test case and expected output
    test_case_match = re.search(r'Sample Input:\s*(.*?)\n', generated_question, re.DOTALL)
    expected_output_match = re.search(r'Expected Output:\s*(.*?)\n', generated_question, re.DOTALL)
    if test_case_match and expected_output_match:
        sample_test_case = test_case_match.group(1).strip()
        expected_output = expected_output_match.group(1).strip()
    else:
        sample_test_case = ""
        expected_output = ""

    return code_template, sample_test_case, expected_output

# Move the input form to the sidebar to make it always visible and more compact
with st.sidebar.form(key="input_form"):
    st.markdown("## Generate a New Question")
    company = st.text_input("Company", value="Google")  # Default value: Google
    difficulty = st.selectbox("Difficulty", ["Easy", "Medium", "Hard"], index=1)  # Default: Medium
    topic = st.text_input("Topic", value="Binary Search")  # Default: Binary Search
    generate_button = st.form_submit_button(label="Generate")

if generate_button:
    # Clear session state and start fresh with follow-up mode disabled
    st.session_state.messages = []
    st.session_state.follow_up_mode = False

    # Create a query from user inputs and find the most relevant question
    query = f"{company} {difficulty} {topic}"
    top_question = find_top_question(query)
    
    # Prepare a detailed prompt for GPT using the top question's details
    detailed_prompt = (
        f"Transform this LeetCode question into a real-world interview scenario.\n\n"
        f"**Company**: {top_question['company']}\n"
        f"**Question Name**: {top_question['questionName']}\n"
        f"**Difficulty Level**: {top_question['difficulty level']}\n"
        f"**Tags**: {top_question['Tags']}\n"
        f"**Content**: {top_question['Content']}\n"
        f"\nPlease create a real-world interview question based on this information. "
        f"Include the following sections:\n\n"
        f"- Problem Description\n"
        f"- Code Template (in a Python code block)\n"
        f"- Sample Input and Expected Output (clearly separated)\n"
    )
    
    # Generate response using OpenAI API with detailed prompt and debugging logs
    response = generate_response([{"role": "user", "content": detailed_prompt}])  # Question generation prompt excluded here

    # Store generated question in session state for persistence in sidebar and follow-up conversation state
    st.session_state.generated_question = response

    # Extract code template and sample test case
    code_template, sample_test_case, expected_output = extract_code_and_test_case(response)
    st.session_state.code_template = code_template
    st.session_state.sample_test_case = sample_test_case
    st.session_state.expected_output = expected_output

    # Enable follow-up mode after generating the initial question
    st.session_state.follow_up_mode = True

# Display chat messages from history on app rerun (for subsequent conversation)
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# Chatbox for subsequent conversations with assistant (follow-up mode)
if st.session_state.follow_up_mode:
    if user_input := st.chat_input("Continue your conversation or ask follow-up questions here:"):
        # Display user message in chat message container and add to session history
        with st.chat_message("user"):
            st.markdown(user_input)
        
        st.session_state.messages.append({"role": "user", "content": user_input})

        # Prepare messages to send to the assistant
        # Include the technical interviewer prompt and generated question, but do not display them
        # Add an instruction for the assistant to reply as a real-world interviewer would
        assistant_instruction = (
            "As a real-world interviewer, please reply to the candidate's follow-up questions "
            "specific to the generated interview question, to the point, and in a natural, human-sounding way."
        )

        messages_to_send = [
            {"role": "user", "content": technical_interviewer_prompt},
            {"role": "assistant", "content": st.session_state.generated_question},
            {"role": "user", "content": assistant_instruction}
        ] + st.session_state.messages

        assistant_response = generate_response(messages_to_send)

        with st.chat_message("assistant"):
            st.markdown(assistant_response)
        
        st.session_state.messages.append({"role": "assistant", "content": assistant_response})

st.sidebar.markdown("---")
st.sidebar.markdown("## Generated Question")
if st.session_state.generated_question:
    st.sidebar.markdown(st.session_state.generated_question)
else:
    st.sidebar.markdown("_No question generated yet._")

st.sidebar.markdown("---")
st.sidebar.markdown("## Python Code Interpreter")

# Pre-fill code interpreter with code template after question generation
if st.session_state.code_template:
    code_input = st.sidebar.text_area("Write your Python code here:", value=st.session_state.code_template, height=300)
else:
    code_input = st.sidebar.text_area("Write your Python code here:", height=300)

if st.sidebar.button("Run Code"):
    try:
        # Prepare the code for execution
        exec_globals = {}
        # Create a function wrapper to execute the user's code
        exec(f"def user_solution():\n{code_input}", exec_globals)
        user_solution = exec_globals.get('user_solution', None)
        
        # Prepare sample test case execution
        if st.session_state.sample_test_case:
            # Assume the sample test case is in the format of arguments to the function
            test_case = st.session_state.sample_test_case
            # Evaluate the test case safely
            test_args = eval(test_case)
            if not isinstance(test_args, tuple):
                test_args = (test_args,)
            # Capture the output
            returned_output = user_solution(*test_args)
        else:
            returned_output = user_solution()
        
        # Display the expected output and returned output
        st.sidebar.markdown("### Sample Test Case Result:")
        st.sidebar.markdown(f"**Sample Input:** {st.session_state.sample_test_case}")
        st.sidebar.markdown(f"**Expected Output:** {st.session_state.expected_output}")
        st.sidebar.markdown(f"**Your Output:** {returned_output}")
        
        # Compare outputs
        if str(returned_output) == st.session_state.expected_output:
            st.sidebar.success("Your output matches the expected output!")
        else:
            st.sidebar.error("Your output does not match the expected output.")
    except Exception as e:
        st.sidebar.error(f"Error: {e}")

# Right sidebar toggleable debug logs and code interpreter section
with st.expander("Debug Logs (Toggle On/Off)", expanded=False):
    if st.session_state.debug_logs:
        st.write(st.session_state.debug_logs)