from openai import OpenAI, RateLimitError
import streamlit as st
import time
import os
import re
from typing import Dict, Optional
from datetime import datetime
# Page configuration (moved to top to ensure it's called only once)
st.set_page_config(
page_title="LinkedIn Recommendation Generator",
page_icon="👔",
layout="wide",
initial_sidebar_state="collapsed"
)
# Original app CSS
st.markdown("""
""", unsafe_allow_html=True)
def create_star_rating(label, key, help_text=None):
"""Create a custom 5-star rating component"""
with st.container():
st.markdown('
', unsafe_allow_html=True)
col1, col2 = st.columns([3, 2])
with col1:
st.markdown(f'
{label}
', unsafe_allow_html=True)
if help_text:
st.caption(help_text)
with col2:
pass
rating = st.select_slider(
"",
options=[1, 2, 3, 4, 5],
value=3,
key=key,
label_visibility="collapsed"
)
stars = "".join(["⭐" if i < rating else "☆" for i in range(5)])
st.markdown(f"
{stars}
", unsafe_allow_html=True)
return rating
def generate_recommendation(ratings: Dict[str, int], employee_type: str, employee_name: str, relationship: str, time_worked: str, linkedin_url: str) -> Optional[str]:
"""Generate recommendation using OpenRouter API with input summary"""
performance_areas = {
"Technical Competence": {
"Domain Knowledge": ratings['domain'],
"Problem Solving": ratings['problem_solving'],
"Initiative": ratings['initiative']
},
"Professional Skills": {
"Adaptability": ratings['adaptability'],
"Communication": ratings['communication']
},
"Interpersonal Impact": {
"Team Collaboration": ratings['teamwork'],
"Support & Guidance": ratings['support']
},
"Overall Performance": {
"Reliability": ratings['reliability'],
"Overall Contribution": ratings['overall'],
"Growth Potential": ratings['potential']
}
}
category_scores = {}
for category, metrics in performance_areas.items():
category_scores[category] = sum(metrics.values()) / len(metrics)
strengths = [k for k, v in ratings.items() if v >= 4]
analysis_text = ""
for category, score in category_scores.items():
analysis_text += f"\n- {category}: {score:.1f}/5"
recommendation_prompt = f"""
You are an expert in writing professional LinkedIn recommendations.
Your task is to generate a recommendation for {employee_name}.
First, silently analyze the provided performance data. Do not output this analysis.
- Employee: {employee_name}
- Role: {employee_type}
- My Relationship to them: {relationship}
- Duration we worked together: {time_worked}
- Performance Summary by Category:{analysis_text}
- Employee's LinkedIn Profile (for context, do not mention the URL in the output): {linkedin_url or 'Not provided'}
- Key Strengths (rated 4 or 5): {', '.join(strengths) if strengths else 'None specified'}
Now, using that analysis, write a detailed and comprehensive LinkedIn recommendation of 200-250 words. The recommendation must:
- Be complete, with no abrupt endings or incomplete sentences.
- Be professional, warm, and authentic in tone.
- Be free of grammatical errors, spelling mistakes, or awkward phrasing.
- End with a strong, forward-looking statement about the employee's potential.
Instructions for the recommendation:
1. Start by clearly stating the working relationship ({relationship}) and the duration ({time_worked}).
2. Highlight their role as a {employee_type} and their key responsibilities.
3. Weave their key strengths ({', '.join(strengths) if strengths else 'None specified'}) into a brief narrative or specific example that illustrates their positive impact (e.g., how their 'Problem Solving' skills unblocked a project or how their 'Team Collaboration' improved team morale).
4. Conclude with a clear, confident statement about their future potential and value to any organization.
5. Use vivid, descriptive language to make the recommendation personal and human.
6. Ensure the recommendation is a complete text, ending with a full sentence and a period, and meets the 200-250 word requirement.
"""
try:
client_kwargs = {
"base_url": "https://openrouter.ai/api/v1",
"api_key": os.environ.get('OPENROUTER_API_KEY')
}
try:
client = OpenAI(**client_kwargs)
except TypeError as e:
if "proxies" in str(e) or "unexpected keyword argument" in str(e):
import openai
openai.api_key = os.environ.get('OPENROUTER_API_KEY')
openai.base_url = "https://openrouter.ai/api/v1"
client = openai
else:
raise e
try:
final_response = client.chat.completions.create(
model="openai/gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are an expert in writing professional, warm, and authentic LinkedIn recommendations. Ensure the output is complete, polished, and free of errors."},
{"role": "user", "content": recommendation_prompt}
],
max_tokens=400, # Increased to accommodate 200-250 words
temperature=0.75
)
return final_response.choices[0].message.content.strip()
except AttributeError:
final_response = client.ChatCompletion.create(
model="openai/gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are an expert in writing professional, warm, and authentic LinkedIn recommendations. Ensure the output is complete, polished, and free of errors."},
{"role": "user", "content": recommendation_prompt}
],
max_tokens=400, # Increased to accommodate 200-250 words
temperature=0.75
)
return final_response['choices'][0]['message']['content'].strip()
except RateLimitError:
st.error("API rate limit or quota exceeded. Please check your OpenRouter account and billing details.")
return None
except Exception as e:
st.error(f"An error occurred while generating the recommendation: {str(e)}")
st.error(f"Error details: {type(e).__name__}")
return None
def render_header():
"""Renders the main header of the application."""
st.markdown("""
""", unsafe_allow_html=True)
def render_input_form() -> Dict:
"""Renders the input form and returns a dictionary of user inputs."""
st.markdown('', unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
employee_name = st.text_input(
"Employee Name",
key="employee_name",
placeholder="e.g., John Smith"
)
relationship = st.selectbox(
"Your relationship with this person",
["", "Direct Manager", "Senior Manager", "Team Lead", "Colleague", "Project Manager", "Department Head", "HR Manager"],
key="relationship"
)
with col2:
employee_type = st.selectbox(
"Employee Role/Department",
["", "Software Developer", "AI Engineer", "Marketing Specialist", "Sales Representative",
"Project Manager", "Data Analyst", "UI/UX Designer", "Customer Support", "Business Analyst",
"Product Manager", "DevOps Engineer", "Content Creator", "HR Specialist", "Other"],
key="employee_type"
)
time_worked = st.selectbox(
"How long have you worked together?",
["", "Less than 6 months", "6 months - 1 year", "1-2 years", "2-3 years", "3-5 years", "More than 5 years"],
key="time_worked"
)
linkedin_url = st.text_input(
"Enter LinkedIn Profile URL",
key="linkedin_url",
placeholder="e.g., https://www.linkedin.com/in/username"
)
st.markdown('', unsafe_allow_html=True)
st.markdown("*Rate each aspect on a scale of 1-5 stars*")
ratings = {}
st.markdown("", unsafe_allow_html=True)
ratings['domain'] = create_star_rating(
"How would you rate the employee's knowledge and expertise in their specific field or role?",
"domain"
)
ratings['problem_solving'] = create_star_rating(
"How effectively does the employee address challenges and find solutions?",
"problem_solving"
)
ratings['initiative'] = create_star_rating(
"How proactive is the employee in taking initiative and contributing to company objectives?",
"initiative"
)
st.markdown("", unsafe_allow_html=True)
ratings['adaptability'] = create_star_rating(
"How well does the employee handle change or take on new responsibilities?",
"adaptability"
)
ratings['communication'] = create_star_rating(
"How clearly and professionally does the employee communicate ideas or information?",
"communication"
)
st.markdown("", unsafe_allow_html=True)
ratings['teamwork'] = create_star_rating(
"How well does the employee work with colleagues or teams to achieve goals?",
"teamwork"
)
ratings['support'] = create_star_rating(
"How well does the employee support or guide others in the work environment?",
"support"
)
st.markdown("", unsafe_allow_html=True)
ratings['reliability'] = create_star_rating(
"How consistently does the employee demonstrate dedication and reliability?",
"reliability"
)
ratings['overall'] = create_star_rating(
"How would you rate the employee's overall contribution to their role and the team?",
"overall"
)
ratings['potential'] = create_star_rating(
"How would you rate the employee's potential for further growth or advancement within the organization?",
"potential"
)
return {
"employee_name": employee_name,
"relationship": relationship,
"employee_type": employee_type,
"time_worked": time_worked,
"linkedin_url": linkedin_url,
"ratings": ratings
}
def render_results_section(ratings: Dict[str, int]):
"""Renders the recommendation, action buttons, and analytics."""
if st.session_state.recommendation_generated:
st.markdown(f"""
📝 Your LinkedIn Recommendation
{st.session_state.generated_text}
""", unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
if st.button("📋 Show Text for Copying"):
st.code(st.session_state.generated_text, language="text")
st.info("You can now manually copy the text above.")
with col2:
if st.button("🔄 Generate New Version"):
st.session_state.recommendation_generated = False
st.rerun()
if st.session_state.saved_linkedin_url:
st.markdown(f"""
""", unsafe_allow_html=True)
st.markdown("""
📖 How to Post on LinkedIn
- Copy the recommendation text above
- Click on the person's LinkedIn profile
- Click "More" → "Recommend"
- Paste the generated recommendation
- Review and send!
""", unsafe_allow_html=True)
st.markdown('
📊 Rating Summary
', unsafe_allow_html=True)
col1, col2, col3, col4 = st.columns(4)
avg_rating = sum(ratings.values()) / len(ratings)
highest_rating = max(ratings.values())
lowest_rating = min(ratings.values())
with col1:
st.metric("Average Rating", f"{avg_rating:.1f}/5", f"{avg_rating/5*100:.0f}%")
with col2:
st.metric("Highest Rating", f"{highest_rating}/5")
with col3:
st.metric("Lowest Rating", f"{lowest_rating}/5")
with col4:
st.metric("Word Count", len(st.session_state.generated_text.split()))
def main():
"""Main function to run the Streamlit application."""
# Check for API key
api_key = os.environ.get('OPENROUTER_API_KEY')
if not api_key:
try:
if hasattr(st, 'secrets') and 'OPENROUTER_API_KEY' in st.secrets:
api_key = st.secrets['OPENROUTER_API_KEY']
os.environ['OPENROUTER_API_KEY'] = api_key
except Exception:
pass
if not api_key:
st.error("🔑 OpenRouter API key not found. Please add it to your Hugging Face Space secrets in the 'Settings' tab.")
st.info("Debug: Check that your OPENROUTER_API_KEY is properly set in the Hugging Face Space settings under 'Repository secrets'.")
st.stop()
render_header()
# Initialize session state
if 'recommendation_generated' not in st.session_state:
st.session_state.recommendation_generated = False
if 'generated_text' not in st.session_state:
st.session_state.generated_text = ""
if 'saved_linkedin_url' not in st.session_state:
st.session_state.saved_linkedin_url = ""
form_data = render_input_form()
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
if st.button("🚀 Generate LinkedIn Recommendation", type="primary"):
required_fields = ["employee_name", "employee_type", "relationship", "time_worked"]
if not all(form_data[field] for field in required_fields):
st.error("Please fill in all required fields in the 'Basic Information' section.")
else:
with st.spinner("🤖 Analyzing performance data and crafting your recommendation..."):
progress_bar = st.progress(0, text="Analyzing...")
time.sleep(0.5)
progress_bar.progress(50, text="Generating text...")
recommendation = generate_recommendation(**form_data)
# Validate recommendation
if recommendation:
word_count = len(recommendation.split())
# Check if recommendation is within 150-300 words and ends with a period
if word_count < 150 or not recommendation.strip().endswith('.'):
st.error("The generated recommendation is incomplete or too short. Please try again.")
st.session_state.recommendation_generated = False
progress_bar.empty()
time.sleep(0.5)
st.rerun()
else:
st.session_state.recommendation_generated = True
st.session_state.generated_text = recommendation
st.session_state.saved_linkedin_url = form_data["linkedin_url"]
st.success("✅ Recommendation generated successfully!")
progress_bar.progress(100, text="Done!")
time.sleep(0.5)
progress_bar.empty()
st.rerun()
else:
progress_bar.empty()
render_results_section(form_data["ratings"])
if __name__ == "__main__":
main()