|
|
|
import os |
|
import streamlit as st |
|
import logging |
|
import queue |
|
import time |
|
import sys |
|
|
|
|
|
sys.path.append('../') |
|
|
|
try: |
|
from main import research_company, generate_interview_questions, create_interview_prep_plan |
|
except ImportError as e: |
|
st.error(f"Error importing functions: {e}") |
|
st.stop() |
|
|
|
|
|
log_queue = queue.Queue() |
|
|
|
class StreamlitLogHandler(logging.Handler): |
|
def __init__(self, log_queue): |
|
super().__init__() |
|
self.log_queue = log_queue |
|
self.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) |
|
def emit(self, record): |
|
log_entry = self.format(record) |
|
self.log_queue.put(log_entry) |
|
|
|
|
|
root_logger = logging.getLogger() |
|
root_logger.setLevel(logging.INFO) |
|
for handler in root_logger.handlers[:]: |
|
root_logger.removeHandler(handler) |
|
root_logger.addHandler(StreamlitLogHandler(log_queue)) |
|
root_logger.addHandler(logging.StreamHandler()) |
|
|
|
|
|
st.set_page_config( |
|
page_title="Interview Prep Assistant(With OWL π¦)", |
|
page_icon="π¦", |
|
layout="wide" |
|
) |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.main-title { |
|
font-size: 2.5rem; |
|
color: #4a89dc; |
|
text-align: center; |
|
margin-bottom: 1rem; |
|
} |
|
.sub-title { |
|
font-size: 1.2rem; |
|
color: #666; |
|
text-align: center; |
|
margin-bottom: 2rem; |
|
} |
|
.conversation-container { |
|
border: 1px solid #e0e0e0; |
|
border-radius: 8px; |
|
margin: 10px 0; |
|
padding: 10px; |
|
max-height: 500px; |
|
overflow-y: auto; |
|
} |
|
.user-message { |
|
background-color: #f0f7ff; |
|
border-left: 4px solid #4a89dc; |
|
padding: 10px; |
|
margin: 8px 0; |
|
border-radius: 4px; |
|
} |
|
.assistant-message { |
|
background-color: #f1f8e9; |
|
border-left: 4px solid #7cb342; |
|
padding: 10px; |
|
margin: 8px 0; |
|
border-radius: 4px; |
|
} |
|
.tool-call { |
|
background-color: #fff8e1; |
|
border: 1px solid #ffe0b2; |
|
padding: 8px; |
|
margin: 5px 0; |
|
border-radius: 4px; |
|
font-family: monospace; |
|
font-size: 0.9em; |
|
} |
|
.round-header { |
|
background-color: #e8eaf6; |
|
padding: 5px 10px; |
|
font-weight: bold; |
|
border-radius: 4px; |
|
margin: 15px 0 5px 0; |
|
} |
|
.final-answer { |
|
background-color: #e8f5e9; |
|
border-left: 5px solid #43a047; |
|
padding: 15px; |
|
margin: 15px 0; |
|
border-radius: 4px; |
|
} |
|
.metrics-container { |
|
display: flex; |
|
justify-content: space-around; |
|
margin: 15px 0; |
|
padding: 10px; |
|
background-color: #f5f5f5; |
|
border-radius: 4px; |
|
} |
|
.metric-box { |
|
text-align: center; |
|
padding: 8px 15px; |
|
background-color: white; |
|
border-radius: 8px; |
|
box-shadow: 0 1px 3px rgba(0,0,0,0.12); |
|
} |
|
.metric-value { |
|
font-size: 1.4rem; |
|
font-weight: bold; |
|
color: #4a89dc; |
|
} |
|
.metric-label { |
|
font-size: 0.8rem; |
|
color: #666; |
|
} |
|
.running-indicator { |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
gap: 10px; |
|
margin: 15px 0; |
|
padding: 10px; |
|
background-color: #e3f2fd; |
|
border-radius: 4px; |
|
animation: pulse 2s infinite; |
|
} |
|
@keyframes pulse { |
|
0% { opacity: 1; } |
|
50% { opacity: 0.7; } |
|
100% { opacity: 1; } |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
def display_conversation(chat_history): |
|
"""Display the conversation history in a structured format""" |
|
if not chat_history: |
|
st.info("No conversation available") |
|
return |
|
|
|
st.markdown("<div class='conversation-container'>", unsafe_allow_html=True) |
|
|
|
for idx, message in enumerate(chat_history): |
|
round_num = idx + 1 |
|
st.markdown(f"<div class='round-header'>Round {round_num}</div>", unsafe_allow_html=True) |
|
|
|
|
|
if "user" in message and message["user"]: |
|
st.markdown(f"<div class='user-message'><b>π§βπΌ Job Seeker:</b><br>{message['user']}</div>", unsafe_allow_html=True) |
|
|
|
|
|
if "assistant" in message and message["assistant"]: |
|
assistant_content = message["assistant"] |
|
|
|
if "[Note: This conversation was limited" in assistant_content: |
|
assistant_content = assistant_content.replace("[Note: This conversation was limited to maintain response quality. The complete thought process is available in the logs.]", "") |
|
|
|
st.markdown(f"<div class='assistant-message'><b>π¦ Interview Coach:</b><br>{assistant_content}</div>", unsafe_allow_html=True) |
|
|
|
|
|
if "tool_calls" in message and message["tool_calls"]: |
|
for tool in message["tool_calls"]: |
|
tool_name = tool.get('name', 'Unknown Tool') |
|
st.markdown(f"<div class='tool-call'><b>π§ Tool Used: {tool_name}</b></div>", unsafe_allow_html=True) |
|
|
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
def display_metrics(duration, token_count, num_rounds): |
|
"""Display metrics in a visually appealing format""" |
|
st.markdown("<div class='metrics-container'>", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(f""" |
|
<div class='metric-box'> |
|
<div class='metric-value'>{duration:.1f}s</div> |
|
<div class='metric-label'>Execution Time</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
completion_tokens = token_count.get('completion_token_count', 0) |
|
prompt_tokens = token_count.get('prompt_token_count', 0) |
|
total_tokens = completion_tokens + prompt_tokens |
|
|
|
st.markdown(f""" |
|
<div class='metric-box'> |
|
<div class='metric-value'>{total_tokens:,}</div> |
|
<div class='metric-label'>Total Tokens</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
st.markdown(f""" |
|
<div class='metric-box'> |
|
<div class='metric-value'>{num_rounds}</div> |
|
<div class='metric-label'>Conversation Rounds</div> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
|
|
st.markdown("</div>", unsafe_allow_html=True) |
|
|
|
def get_logs(): |
|
"""Retrieve logs from the queue""" |
|
logs = [] |
|
while not log_queue.empty(): |
|
try: |
|
logs.append(log_queue.get_nowait()) |
|
except queue.Empty: |
|
break |
|
return logs |
|
|
|
def main(): |
|
|
|
st.markdown("<h1 class='main-title'>π¦ Interview Preparation Assistant</h1>", unsafe_allow_html=True) |
|
st.markdown("<p class='sub-title'>Powered by multi-agent AI collaboration</p>", unsafe_allow_html=True) |
|
|
|
|
|
with st.container(): |
|
col1, col2 = st.columns(2) |
|
with col1: |
|
job_role = st.text_input("Job Role", "Machine Learning Engineer") |
|
with col2: |
|
company_name = st.text_input("Company Name", "Google") |
|
|
|
|
|
tab1, tab2, tab3, tab4 = st.tabs(["Company Research", "Interview Questions", "Preparation Plan", "System Logs"]) |
|
|
|
|
|
with tab1: |
|
st.header("π Company Research") |
|
st.write("Get detailed insights about the company to help with your interview preparation.") |
|
|
|
if st.button("Research Company", use_container_width=True): |
|
with st.spinner(): |
|
|
|
status = st.empty() |
|
status.markdown("<div class='running-indicator'>π Researching company information...</div>", unsafe_allow_html=True) |
|
|
|
|
|
progress = st.progress(0) |
|
|
|
|
|
def update_progress(current_round, max_rounds): |
|
progress_value = min(current_round / max_rounds, 0.95) |
|
progress.progress(progress_value) |
|
status.markdown(f"<div class='running-indicator'>π Processing conversation round {current_round}/{max_rounds}...</div>", unsafe_allow_html=True) |
|
|
|
|
|
try: |
|
start_time = time.time() |
|
result = research_company( |
|
company_name=company_name, |
|
detailed=True, |
|
limited_searches=False, |
|
progress_callback=update_progress |
|
) |
|
duration = time.time() - start_time |
|
|
|
|
|
progress.progress(1.0) |
|
status.markdown("<div class='running-indicator' style='background-color: #e8f5e9;'>β
Research completed!</div>", unsafe_allow_html=True) |
|
|
|
|
|
display_metrics( |
|
duration=duration, |
|
token_count=result["token_count"], |
|
num_rounds=len(result["chat_history"]) |
|
) |
|
|
|
|
|
st.subheader("π Research Results") |
|
st.markdown(f"<div class='final-answer'>{result['answer']}</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.subheader("π¬ Conversation Process") |
|
display_conversation(result["chat_history"]) |
|
|
|
except Exception as e: |
|
st.error(f"Error: {str(e)}") |
|
logging.exception("Error in company research") |
|
|
|
|
|
with tab2: |
|
st.header("β Interview Questions") |
|
st.write("Generate tailored interview questions for your target role and company.") |
|
|
|
|
|
question_type = st.radio( |
|
"Question Type", |
|
["Technical", "Behavioral", "Company-Specific", "All"], |
|
horizontal=True |
|
) |
|
|
|
if st.button("Generate Questions", use_container_width=True): |
|
with st.spinner(): |
|
|
|
status = st.empty() |
|
status.markdown("<div class='running-indicator'>π Creating interview questions...</div>", unsafe_allow_html=True) |
|
|
|
|
|
progress = st.progress(0) |
|
|
|
|
|
def update_progress(current_round, max_rounds): |
|
progress_value = min(current_round / max_rounds, 0.95) |
|
progress.progress(progress_value) |
|
status.markdown(f"<div class='running-indicator'>π Processing conversation round {current_round}/{max_rounds}...</div>", unsafe_allow_html=True) |
|
|
|
|
|
try: |
|
start_time = time.time() |
|
result = generate_interview_questions( |
|
job_role=job_role, |
|
company_name=company_name, |
|
detailed=True, |
|
limited_searches=False, |
|
progress_callback=update_progress |
|
) |
|
duration = time.time() - start_time |
|
|
|
|
|
progress.progress(1.0) |
|
status.markdown("<div class='running-indicator' style='background-color: #e8f5e9;'>β
Questions generated!</div>", unsafe_allow_html=True) |
|
|
|
|
|
display_metrics( |
|
duration=duration, |
|
token_count=result["token_count"], |
|
num_rounds=len(result["chat_history"]) |
|
) |
|
|
|
|
|
st.subheader("π Generated Questions") |
|
st.markdown(f"<div class='final-answer'>{result['answer']}</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.subheader("π¬ Conversation Process") |
|
display_conversation(result["chat_history"]) |
|
|
|
except Exception as e: |
|
st.error(f"Error: {str(e)}") |
|
logging.exception("Error in question generation") |
|
|
|
|
|
with tab3: |
|
st.header("π Interview Preparation Plan") |
|
st.write("Create a comprehensive step-by-step plan to prepare for your interview.") |
|
|
|
if st.button("Create Preparation Plan", use_container_width=True): |
|
with st.spinner(): |
|
|
|
status = st.empty() |
|
status.markdown("<div class='running-indicator'>π Creating preparation plan...</div>", unsafe_allow_html=True) |
|
|
|
|
|
progress = st.progress(0) |
|
|
|
|
|
def update_progress(current_round, max_rounds): |
|
progress_value = min(current_round / max_rounds, 0.95) |
|
progress.progress(progress_value) |
|
status.markdown(f"<div class='running-indicator'>π Processing conversation round {current_round}/{max_rounds}...</div>", unsafe_allow_html=True) |
|
|
|
|
|
try: |
|
start_time = time.time() |
|
result = create_interview_prep_plan( |
|
job_role=job_role, |
|
company_name=company_name, |
|
detailed=True, |
|
limited_searches=False, |
|
progress_callback=update_progress |
|
) |
|
duration = time.time() - start_time |
|
|
|
|
|
progress.progress(1.0) |
|
status.markdown("<div class='running-indicator' style='background-color: #e8f5e9;'>β
Plan created!</div>", unsafe_allow_html=True) |
|
|
|
|
|
display_metrics( |
|
duration=duration, |
|
token_count=result["token_count"], |
|
num_rounds=len(result["chat_history"]) |
|
) |
|
|
|
|
|
st.subheader("π Preparation Plan") |
|
st.markdown(f"<div class='final-answer'>{result['answer']}</div>", unsafe_allow_html=True) |
|
|
|
|
|
st.subheader("π¬ Conversation Process") |
|
display_conversation(result["chat_history"]) |
|
|
|
except Exception as e: |
|
st.error(f"Error: {str(e)}") |
|
logging.exception("Error in preparation plan creation") |
|
|
|
|
|
with tab4: |
|
st.header("π§ System Logs") |
|
st.write("View detailed system logs for debugging.") |
|
|
|
logs_container = st.empty() |
|
|
|
|
|
logs = get_logs() |
|
if logs: |
|
logs_container.code("\n".join(logs)) |
|
else: |
|
logs_container.info("No logs available yet.") |
|
|
|
|
|
if st.button("Refresh Logs"): |
|
logs = get_logs() |
|
if logs: |
|
logs_container.code("\n".join(logs)) |
|
else: |
|
logs_container.info("No logs available yet.") |
|
|
|
|
|
auto_refresh = st.checkbox("Auto-refresh logs", value=True) |
|
if auto_refresh: |
|
st.markdown( |
|
""" |
|
<script> |
|
function refreshLogs() { |
|
const checkbox = document.querySelector('.stCheckbox input[type="checkbox"]'); |
|
if (checkbox && checkbox.checked) { |
|
const refreshButton = document.querySelector('button:contains("Refresh Logs")'); |
|
if (refreshButton) refreshButton.click(); |
|
} |
|
setTimeout(refreshLogs, 3000); |
|
} |
|
setTimeout(refreshLogs, 3000); |
|
</script> |
|
""", |
|
unsafe_allow_html=True |
|
) |
|
|
|
if __name__ == "__main__": |
|
try: |
|
logging.info("Starting Interview Preparation Assistant application") |
|
main() |
|
except Exception as e: |
|
st.error(f"Application error: {str(e)}") |
|
logging.exception("Application error") |