""" Career Agent - Intelligent CV and Cover Letter Generator ======================================================== An AI agent that autonomously analyzes job postings and candidate profiles to create tailored application documents using multi-step reasoning. Author: Career Agent Team Version: 1.2 - Modern UI Edition """ import os import logging import gradio as gr from gradio.themes.soft import Soft from gradio.themes import GoogleFont from typing import Optional, Dict, Any from dotenv import load_dotenv from openai import OpenAI import time # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Load environment variables load_dotenv() # === SambaNova API Integration === class SambaNovaClient: """SambaNova client using OpenAI SDK.""" def __init__(self, api_key: str, model_name: str = "Meta-Llama-3.3-70B-Instruct"): self.client = OpenAI( api_key=api_key, base_url="https://api.sambanova.ai/v1" ) self.model_name = model_name def generate(self, prompt: str, system_prompt: str = "You are a helpful assistant.") -> str: """Generate response using SambaNova API.""" try: response = self.client.chat.completions.create( model=self.model_name, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt} ], temperature=0.1, top_p=0.1, max_tokens=3000 ) return response.choices[0].message.content or "" except Exception as e: logger.error(f"SambaNova API call failed: {e}") return f"❌ API Error: {str(e)}" # === Analysis Functions === def analyze_candidate_profile(client: SambaNovaClient, user_info: str) -> str: """Analyzes the candidate's profile to extract insights and positioning.""" system_prompt = "You are a senior career strategist with expertise in talent assessment and professional positioning." prompt = f""" Analyze this candidate profile in detail: {user_info} Provide a comprehensive analysis including: - Experience level and career stage assessment - Core competencies and unique value propositions - Industry expertise and domain knowledge - Leadership capabilities and soft skills evidence - Career trajectory and growth potential - Strategic positioning opportunities - Areas of competitive advantage Format your response as a structured professional assessment with clear sections. """ return client.generate(prompt, system_prompt) def research_job_posting(client: SambaNovaClient, job_description: str) -> str: """Analyzes the job posting for strategic application guidance.""" system_prompt = "You are an expert recruiter and talent acquisition specialist with deep knowledge of job market trends." prompt = f""" Conduct a comprehensive analysis of this job posting: {job_description} Extract and analyze: - Must-have vs nice-to-have qualifications - Technical skills and experience requirements breakdown - Company culture indicators and values alignment - Key responsibilities and success metrics - Career growth and advancement signals - Compensation and benefits analysis - Critical ATS keywords and industry terminology - Hiring urgency and competition level indicators - Decision-maker priorities and pain points Provide strategic intelligence for optimal application positioning. """ return client.generate(prompt, system_prompt) def assess_fit_and_strategy(client: SambaNovaClient, profile_analysis: str, job_research: str) -> str: """Evaluates fit between candidate and job, develops application strategy.""" system_prompt = "You are a strategic career consultant specializing in job application optimization and candidate positioning." prompt = f""" Based on the candidate analysis and job research below, develop a comprehensive application strategy: CANDIDATE ANALYSIS: {profile_analysis} JOB INTELLIGENCE: {job_research} Provide detailed strategic assessment: - Overall fit percentage with detailed justification - Top 5 strongest selling points to emphasize - Potential concerns and mitigation strategies - Unique value proposition development - Competitive differentiation approach - Key messaging themes and positioning - Recommended application tone and style - Interview preparation insights """ return client.generate(prompt, system_prompt) def generate_strategic_resume(client: SambaNovaClient, candidate_info: str, strategy: str) -> str: """Generates a tailored, ATS-optimized resume.""" system_prompt = "You are a professional resume writer and ATS optimization expert with extensive experience in creating winning resumes." prompt = f""" Create a professional, ATS-optimized resume based on: CANDIDATE INFORMATION: {candidate_info} STRATEGIC GUIDANCE: {strategy} Generate a complete resume with: - Compelling professional summary (3-4 lines) - Core competencies/skills section optimized for ATS - Professional experience with quantified achievements - Education and certifications - Additional relevant sections (projects, awards, etc.) Format professionally with clear sections and bullet points. Focus on impact, metrics, and value delivery. """ return client.generate(prompt, system_prompt) def create_cover_letter(client: SambaNovaClient, candidate_info: str, strategy: str, job_description: str) -> str: """Generates a persuasive, personalized cover letter.""" system_prompt = "You are an expert cover letter writer specializing in compelling, personalized application letters that get results." prompt = f""" Write a persuasive cover letter based on: CANDIDATE INFORMATION: {candidate_info} STRATEGIC POSITIONING: {strategy} JOB DESCRIPTION: {job_description} Create an engaging cover letter that: - Opens with a compelling hook that grabs attention - Demonstrates specific research about the company/role - Connects candidate experience to concrete value delivery - Shows cultural fit and genuine enthusiasm - Addresses potential concerns proactively - Closes with a confident, action-oriented call-to-action Keep it concise (3-4 paragraphs) but impactful. """ return client.generate(prompt, system_prompt) def quality_assurance_check(client: SambaNovaClient, resume: str, cover_letter: str, job_posting: str) -> str: """Performs quality review of generated documents.""" system_prompt = "You are a senior HR professional and application reviewer with expertise in document quality assessment." prompt = f""" Perform a comprehensive quality review of these application materials: RESUME: {resume} COVER LETTER: {cover_letter} ORIGINAL JOB POSTING: {job_posting} Evaluate and provide feedback on: - ATS optimization and keyword alignment (score /10) - Document consistency and professional presentation (score /10) - Value proposition clarity and impact (score /10) - Competitive differentiation strength (score /10) - Overall application effectiveness (score /10) Provide specific improvement recommendations and an overall quality assessment. """ return client.generate(prompt, system_prompt) # === Main Career Agent Class === class CareerAgent: """Main Career Agent class handling AI-powered document generation.""" def __init__(self): self.client = self._initialize_client() def _initialize_client(self) -> SambaNovaClient: """Initialize SambaNova client.""" api_key = os.getenv("SAMBANOVA_API_KEY") if not api_key: raise ValueError("SAMBANOVA_API_KEY environment variable is required.") return SambaNovaClient(api_key=api_key) def process_application(self, user_info: str, job_description: str, progress=gr.Progress()) -> str: """Process the complete application with step-by-step analysis.""" if not user_info.strip() or not job_description.strip(): return "❌ Please provide both your profile and the job description." try: progress(0.1, desc="🔍 Analyzing your profile...") profile_analysis = analyze_candidate_profile(self.client, user_info) time.sleep(0.5) progress(0.3, desc="🎯 Researching job requirements...") job_research = research_job_posting(self.client, job_description) time.sleep(0.5) progress(0.5, desc="🧠 Developing strategy...") strategy = assess_fit_and_strategy(self.client, profile_analysis, job_research) time.sleep(0.5) progress(0.7, desc="📄 Generating tailored resume...") resume = generate_strategic_resume(self.client, user_info, strategy) time.sleep(0.5) progress(0.85, desc="✉️ Creating cover letter...") cover_letter = create_cover_letter(self.client, user_info, strategy, job_description) time.sleep(0.5) progress(0.95, desc="✅ Quality assurance check...") qa_review = quality_assurance_check(self.client, resume, cover_letter, job_description) progress(1.0, desc="🎉 Complete!") # Format final output result = f""" # 📄 TAILORED RESUME {resume} --- # ✉️ PERSONALIZED COVER LETTER {cover_letter} --- # 🎯 STRATEGIC ANALYSIS {strategy} --- # ✅ QUALITY ASSURANCE REVIEW {qa_review} --- *Generated by SambaNova Llama 3.3 70B Instruct* """ logger.info("✅ Application processing completed successfully!") return result.strip() except Exception as e: logger.error(f"Processing error: {e}") return f"""❌ **Error occurred:** {str(e)} 🔧 **Troubleshooting Steps:** - Verify your SAMBANOVA_API_KEY is valid - Check your internet connection - Ensure you have sufficient API credits - Try again in a few moments If the problem persists, please check your API key configuration.""" # === Modern Gradio Interface === class ModernCareerInterface: def __init__(self, agent: CareerAgent): self.agent = agent def validate_api_key(self, api_key): """Validate API SambaNova API key""" if not api_key or not api_key.strip(): return gr.HTML("""
🔐 Enter your SambaNova API key to get started
""") try: # Test simple de la clé API test_client = SambaNovaClient(api_key=api_key.strip()) return gr.HTML("""
✅ API key validated successfully
""") except Exception as e: return gr.HTML(f"""
❌ API key validation failed: {str(e)[:50]}...
""") def create_interface(self) -> gr.Blocks: """Create a modern, beautiful dark interface.""" theme = Soft( primary_hue="blue", secondary_hue="slate", font=GoogleFont("Inter") ).set( body_background_fill="linear-gradient(135deg, #667eea 0%, #764ba2 100%)", block_background_fill="rgba(30, 41, 59, 0.95)", # Dark theme block_border_width="1px", block_border_color="rgba(148, 163, 184, 0.2)", block_shadow="0 20px 25px -5px rgba(0, 0, 0, 0.25), 0 10px 10px -5px rgba(0, 0, 0, 0.1)", block_radius="16px", button_primary_background_fill="linear-gradient(135deg, #667eea 0%, #764ba2 100%)", button_primary_background_fill_hover="linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%)", button_primary_text_color="white", input_background_fill="rgba(51, 65, 85, 0.8)", # Dark input background input_border_color="rgba(148, 163, 184, 0.3)", input_border_width="1px", input_radius="12px" ) with gr.Blocks(theme=theme, title="🚀 AI Career Agent", css=self._dark_modern_css()) as demo: # Hero Header with gr.Row(elem_classes="hero-header"): with gr.Column(scale=1): gr.HTML("""
🚀

AI Career Agent

Generate winning resumes & cover letters in minutes

✨ AI-Powered 🎯 ATS-Optimized ⚡ Lightning Fast
""") # Status Bar status_bar = gr.HTML("""
🟢 Ready - Llama 3.3 70B Model Loaded
""") # API Key Input Section with gr.Row(elem_classes="api-key-section"): with gr.Column(): gr.HTML('
🔑 SambaNova API Configuration
') with gr.Group(elem_classes="api-input-group"): api_key_input = gr.Textbox( label="SambaNova API Key", placeholder="Enter your SambaNova API key here (sk-...)", type="password", value=os.getenv("SAMBANOVA_API_KEY", ""), info="Your API key is used securely and not stored permanently. Get your free key at: https://sambanova.ai/", elem_classes="api-key-input", container=True, show_label=True, interactive=True ) api_status = gr.HTML("""
🔐 Enter your SambaNova API key to get started
""") # Main Content Area with gr.Row(elem_classes="main-content"): # Left Panel - Input with gr.Column(scale=1, elem_classes="input-panel"): gr.HTML('
📝 Your Information
') # Profile Input with gr.Group(elem_classes="input-group"): gr.HTML('
👤 Professional Profile
') user_info = gr.Textbox( placeholder="""Tell me about yourself! Include: • Your current role and experience level • Key skills and technologies you know • Notable achievements (with numbers/metrics) • Education and certifications • Career goals and interests Example: Senior Software Engineer with 5+ years experience in full-stack development. Expert in Python, React, and AWS. Led a team of 8 developers at TechCorp, increasing deployment efficiency by 40%. MBA in Technology Management from Stanford. Passionate about AI and machine learning...""", lines=8, max_lines=12, container=False, show_label=False, elem_classes="modern-input" ) # Job Description Input with gr.Group(elem_classes="input-group"): gr.HTML('
📄 Target Job Description
') job_description = gr.Textbox( placeholder="""Paste the complete job posting here: Include everything you can find: • Job title and company name • Required qualifications and skills • Job responsibilities and duties • Company culture information • Benefits and compensation details • Any other relevant information The more details you provide, the better I can tailor your application!""", lines=6, max_lines=10, container=False, show_label=False, elem_classes="modern-input" ) # Action Buttons with gr.Row(elem_classes="button-row"): generate_btn = gr.Button( "🚀 Generate My Application", variant="primary", size="lg", elem_classes="generate-button" ) clear_btn = gr.Button( "🗑️ Clear All", variant="secondary", elem_classes="clear-button" ) # Right Panel - Output with gr.Column(scale=1, elem_classes="output-panel"): gr.HTML('
📄 Generated Documents
') # Output Display with gr.Group(elem_classes="output-group"): output = gr.Markdown( value="### 🎯 Your personalized application materials will appear here\n\nClick **'Generate My Application'** to get started with your AI-powered resume and cover letter!", elem_classes="output-display", show_label=False, container=False ) # Tips Section with gr.Row(elem_classes="tips-section"): with gr.Column(): gr.HTML("""

💡 Pro Tips for Best Results

📊
Use Numbers

Include specific metrics and achievements

🔑
Match Keywords

Include relevant industry terminology

📋
Complete Details

Provide full job description for better matching

Review & Customize

Always personalize the final output

""") # Processing Steps Indicator with gr.Row(elem_classes="steps-section", visible=False) as steps_row: gr.HTML("""
🔄 Processing Steps
1
Profile Analysis
2
Job Research
3
Strategy Development
4
Resume Generation
5
Cover Letter
6
Quality Review
""") # Event Handlers def update_status_processing(): return gr.HTML("""
🔄 Processing... SambaNova Llama 3.3 70B is crafting your application
""") def update_status_complete(): return gr.HTML("""
Complete! Your application materials are ready
""") def update_status_error(): return gr.HTML("""
Error Please check your inputs and try again
""") def process_with_status(user_info, job_desc, api_key, progress=gr.Progress()): try: result = self.agent.process_application(user_info, job_desc, progress) return result, update_status_complete(), gr.Row(visible=False) except Exception as e: error_msg = f"❌ **Error:** {str(e)}\n\nPlease check your API key and try again." return error_msg, update_status_error(), gr.Row(visible=False) def clear_all(): return ( "", # user_info "", # job_description "", # api_key_input - vider aussi la clé API "### 🎯 Your personalized application materials will appear here...", gr.HTML("""
...
"""), gr.Row(visible=False), gr.HTML("""
...
""") # Reset API status ) # Button Events generate_btn.click( fn=lambda: (update_status_processing(), gr.Row(visible=True)), outputs=[status_bar, steps_row] ).then( fn=process_with_status, inputs=[user_info, job_description, api_key_input], # Ajouter api_key_input outputs=[output, status_bar, steps_row] ) clear_btn.click( fn=clear_all, outputs=[user_info, job_description, api_key_input, output, status_bar, steps_row, api_status] ) api_key_input.change( fn=self.validate_api_key, # Notez le 'self.' ici inputs=[api_key_input], outputs=[api_status] ) return demo def _dark_modern_css(self) -> str: """Modern, beautiful dark theme CSS styling.""" return """ /* Global Styles */ .gradio-container { max-width: 1600px !important; margin: 0 auto !important; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important; } /* Hero Header - Kept beautiful and bright */ .hero-header { background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); border-radius: 20px; padding: 2rem; margin-bottom: 2rem; border: 1px solid rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); } .hero-content { text-align: center; color: white; } .hero-icon { font-size: 4rem; margin-bottom: 1rem; filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.1)); } .hero-title { font-size: 3rem; font-weight: 800; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .hero-subtitle { font-size: 1.25rem; color: rgba(255, 255, 255, 0.9); margin: 0.5rem 0 1.5rem 0; font-weight: 400; } .hero-badges { display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; } .badge { background: rgba(255, 255, 255, 0.15); color: white; padding: 0.5rem 1rem; border-radius: 50px; font-size: 0.875rem; font-weight: 500; border: 1px solid rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); } /* Status Bar */ .status-bar { display: flex; align-items: center; gap: 0.75rem; padding: 1rem 1.5rem; border-radius: 12px; margin-bottom: 2rem; font-weight: 500; transition: all 0.3s ease; } .status-ready { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; box-shadow: 0 4px 6px -1px rgba(16, 185, 129, 0.25); } .status-processing { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white; box-shadow: 0 4px 6px -1px rgba(245, 158, 11, 0.25); } .status-complete { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; box-shadow: 0 4px 6px -1px rgba(16, 185, 129, 0.25); } .status-error { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; box-shadow: 0 4px 6px -1px rgba(239, 68, 68, 0.25); } .status-indicator { width: 12px; height: 12px; border-radius: 50%; background: rgba(255, 255, 255, 0.8); } .status-indicator.processing { animation: pulse 2s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } /* Main Content - Dark Theme */ .main-content { gap: 2rem !important; } .input-panel, .output-panel { background: rgba(30, 41, 59, 0.95) !important; border-radius: 20px; padding: 2rem; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.25), 0 10px 10px -5px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(148, 163, 184, 0.2); } .panel-header { font-size: 1.5rem; font-weight: 700; color: #f1f5f9 !important; margin-bottom: 1.5rem; display: flex; align-items: center; gap: 0.5rem; } /* Input Groups - Dark Theme */ .input-group { margin-bottom: 1.5rem; } .input-label { font-size: 1.1rem; font-weight: 600; color: #e2e8f0 !important; margin-bottom: 0.75rem; display: flex; align-items: center; gap: 0.5rem; } .modern-input textarea { background: rgba(51, 65, 85, 0.8) !important; border: 2px solid rgba(148, 163, 184, 0.3) !important; border-radius: 12px !important; padding: 1rem !important; font-size: 0.95rem !important; line-height: 1.6 !important; color: #f1f5f9 !important; transition: all 0.3s ease !important; } .modern-input textarea::placeholder { color: #94a3b8 !important; opacity: 0.8; } .modern-input textarea:focus { border-color: #667eea !important; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2) !important; background: rgba(51, 65, 85, 0.95) !important; } /* Buttons */ .button-row { gap: 1rem !important; margin-top: 2rem; } .generate-button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; border: none !important; color: white !important; font-weight: 600 !important; padding: 0.75rem 2rem !important; border-radius: 12px !important; font-size: 1.1rem !important; box-shadow: 0 4px 6px -1px rgba(102, 126, 234, 0.25) !important; transition: all 0.3s ease !important; } .generate-button:hover { background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%) !important; transform: translateY(-1px) !important; box-shadow: 0 8px 12px -2px rgba(102, 126, 234, 0.35) !important; } .clear-button { background: rgba(148, 163, 184, 0.15) !important; border: 2px solid rgba(148, 163, 184, 0.3) !important; color: #e2e8f0 !important; font-weight: 500 !important; padding: 0.75rem 1.5rem !important; border-radius: 12px !important; transition: all 0.3s ease !important; } .clear-button:hover { background: rgba(148, 163, 184, 0.25) !important; border-color: rgba(148, 163, 184, 0.4) !important; } /* Output - Dark Theme */ .output-group { min-height: 400px; } .output-display { background: rgba(51, 65, 85, 0.6) !important; border: 1px solid rgba(148, 163, 184, 0.3) !important; border-radius: 12px !important; padding: 1.5rem !important; min-height: 400px !important; font-size: 0.95rem !important; line-height: 1.6 !important; color: #f1f5f9 !important; } .output-display h1, .output-display h2, .output-display h3, .output-display h4, .output-display h5, .output-display h6 { color: #f1f5f9 !important; } .output-display strong { color: #e2e8f0 !important; } /* Tips Section - Dark Theme */ .tips-section { margin-top: 3rem; } .tips-container { background: rgba(30, 41, 59, 0.95) !important; border-radius: 20px; padding: 2rem; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.25), 0 10px 10px -5px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(148, 163, 184, 0.2); } .tips-container h3 { font-size: 1.5rem; font-weight: 700; color: #f1f5f9 !important; margin-bottom: 1.5rem; text-align: center; } .tips-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem; } .tip-card { display: flex; align-items: flex-start; gap: 1rem; padding: 1.5rem; background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%) !important; border-radius: 12px; border: 1px solid rgba(102, 126, 234, 0.2); transition: all 0.3s ease; } .tip-card:hover { transform: translateY(-2px); box-shadow: 0 8px 25px -5px rgba(102, 126, 234, 0.25); border-color: rgba(102, 126, 234, 0.3); background: linear-gradient(135deg, rgba(102, 126, 234, 0.15) 0%, rgba(118, 75, 162, 0.15) 100%) !important; } .tip-icon { font-size: 2rem; min-width: 2rem; } .tip-content strong { display: block; font-weight: 600; color: #f1f5f9 !important; margin-bottom: 0.25rem; } .tip-content p { margin: 0; color: #cbd5e1 !important; font-size: 0.9rem; line-height: 1.4; } /* Steps Section - Dark Theme */ .steps-section { margin-top: 2rem; } .steps-container { background: rgba(30, 41, 59, 0.95) !important; border-radius: 16px; padding: 2rem; backdrop-filter: blur(10px); border: 1px solid rgba(148, 163, 184, 0.2); } .steps-header { font-size: 1.25rem; font-weight: 600; color: #f1f5f9 !important; margin-bottom: 1.5rem; text-align: center; } .steps-list { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; } .step { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; flex: 1; min-width: 120px; } .step-number { width: 40px; height: 40px; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 1.1rem; box-shadow: 0 4px 6px -1px rgba(102, 126, 234, 0.25); } .step-text { font-size: 0.875rem; font-weight: 500; color: #e2e8f0 !important; text-align: center; line-height: 1.3; } /* Responsive Design */ @media (max-width: 768px) { .hero-title { font-size: 2rem; } .hero-subtitle { font-size: 1rem; } .main-content { flex-direction: column; } .input-panel, .output-panel { padding: 1.5rem; } .tips-grid { grid-template-columns: 1fr; } .steps-list { flex-direction: column; align-items: center; } .step { flex-direction: row; justify-content: flex-start; min-width: auto; width: 100%; max-width: 300px; } } /* Animations */ @keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .input-panel, .output-panel, .tips-container { animation: fadeInUp 0.6s ease-out; } /* Custom Scrollbar - Dark Theme */ .output-display::-webkit-scrollbar { width: 8px; } .output-display::-webkit-scrollbar-track { background: rgba(51, 65, 85, 0.5); border-radius: 4px; } .output-display::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 4px; } .output-display::-webkit-scrollbar-thumb:hover { background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%); } /* Loading States */ .processing .generate-button { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important; cursor: not-allowed !important; } .processing .generate-button:hover { transform: none !important; } /* Focus States for Accessibility */ .modern-input textarea:focus, .generate-button:focus, .clear-button:focus { outline: 2px solid #667eea !important; outline-offset: 2px !important; } /* High Contrast Mode Support */ @media (prefers-contrast: high) { .hero-header, .input-panel, .output-panel, .tips-container, .steps-container { border: 2px solid #fff; } .modern-input textarea { border: 2px solid #fff !important; } } /* Reduced Motion Support */ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* Print Styles */ @media print { .hero-header, .tips-section, .steps-section, .button-row { display: none !important; } .output-display { border: none !important; background: white !important; color: black !important; box-shadow: none !important; } } .api-key-section { margin: 1rem 0 2rem 0; } .api-section-header { font-size: 1.25rem; font-weight: 600; color: #f1f5f9 !important; margin-bottom: 1rem; text-align: center; display: flex; align-items: center; justify-content: center; gap: 0.5rem; } .api-input-group { background: rgba(30, 41, 59, 0.95) !important; border-radius: 16px; padding: 1.5rem; border: 1px solid rgba(148, 163, 184, 0.2); backdrop-filter: blur(10px); } .api-key-input input { background: rgba(51, 65, 85, 0.8) !important; border: 2px solid rgba(148, 163, 184, 0.3) !important; border-radius: 8px !important; padding: 0.75rem !important; color: #f1f5f9 !important; font-family: 'Monaco', 'Menlo', monospace !important; font-size: 0.9rem !important; transition: all 0.3s ease !important; } .api-key-input input:focus { border-color: #667eea !important; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2) !important; background: rgba(51, 65, 85, 0.95) !important; } .api-key-input input::placeholder { color: #94a3b8 !important; opacity: 0.7; } .api-key-input label { color: #e2e8f0 !important; font-weight: 500 !important; margin-bottom: 0.5rem !important; } .api-key-input .gr-form { color: #cbd5e1 !important; font-size: 0.875rem !important; margin-top: 0.5rem !important; } .api-status { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; border-radius: 8px; margin-top: 1rem; font-size: 0.9rem; font-weight: 500; transition: all 0.3s ease; } .api-status-empty { background: linear-gradient(135deg, #64748b 0%, #475569 100%); color: white; } .api-status-valid { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; } .api-status-invalid { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; } .api-status-indicator { width: 8px; height: 8px; border-radius: 50%; background: rgba(255, 255, 255, 0.8); } """ # === Entry Point === def main(): """Main entry point with enhanced error handling.""" try: print("🚀 Initializing Modern Career Agent with SambaNova...") # Check for API key if not os.getenv("SAMBANOVA_API_KEY"): print("❌ Error: SAMBANOVA_API_KEY environment variable not found!") print("Please set your SambaNova API key:") print("export SAMBANOVA_API_KEY='your-api-key-here'") return career_agent = CareerAgent() print("✅ SambaNova Llama 3.3 70B initialized successfully!") interface = ModernCareerInterface(career_agent) demo = interface.create_interface() print("🌐 Launching Modern Gradio Interface...") demo.launch( share=False, show_error=True, favicon_path=None, show_api=False ) except Exception as e: logger.error(f"Startup error: {e}") print(f"❌ Startup Error: {str(e)}") print("\nTroubleshooting:") print("1. Check your SAMBANOVA_API_KEY is valid") print("2. Ensure you have internet connection") print("3. Verify all dependencies are installed: pip install openai gradio python-dotenv") if __name__ == "__main__": main()