import os from dotenv import load_dotenv from huggingface_hub import InferenceClient import re from typing import List import logging # Set up logging logging.basicConfig(filename="llm_errors.log", level=logging.ERROR) # Load environment variables load_dotenv() class LLMHelper: def __init__(self): self.client = InferenceClient( model=os.getenv("HF_MODEL", "HuggingFaceH4/zephyr-7b-beta"), token=os.getenv("HF_TOKEN") ) def generate_text(self, prompt: str, max_tokens: int = 500) -> str: """Generate text from the LLM with error handling and retry""" for attempt in range(2): # Retry once on failure try: response = self.client.text_generation( prompt, max_new_tokens=max_tokens, temperature=0.7, do_sample=True ) return response.strip() except Exception as e: logging.error(f"LLM Error (Attempt {attempt + 1}): {str(e)}") if attempt == 1: return "" return "" def parse_tech_stack(input_str: str) -> List[str]: """Parse tech stack input into a cleaned list of technologies""" if not input_str.strip(): return [] replacements = { "c#": "C#", "c++": "C++", "f#": "F#", "golang": "Go", "js": "JavaScript", "ts": "TypeScript", "nodejs": "Node.js", "node": "Node.js", "reactjs": "React", "vuejs": "Vue.js", "postgresql": "PostgreSQL", "mysql": "MySQL" } techs = re.split(r'[,;/\n]', input_str) cleaned = [] for tech in techs: tech = tech.strip() if tech: tech = tech.lower() tech = replacements.get(tech, tech) if not re.match(r'^[a-z0-9+#]+$', tech): tech = tech.title() if tech not in cleaned and len(tech) > 1: cleaned.append(tech) return cleaned def generate_tech_questions(tech_stack: List[str], years_experience: int) -> List[str]: """Generate technical questions based on tech stack and experience level""" if not tech_stack: return ["Please describe your technical experience."] llm = LLMHelper() difficulty = "beginner" if years_experience < 2 else "intermediate" if years_experience < 5 else "advanced" questions = [] for tech in tech_stack: prompt = f"""Generate exactly 3 technical questions about {tech} for a candidate with {years_experience} years of experience. Difficulty level: {difficulty} Format each question clearly numbered like: 1. [Question about {tech}] 2. [Question about {tech}] 3. [Question about {tech}] The questions should: - Be technical and specific to {tech} - Cover different aspects (syntax, architecture, debugging) - Require detailed answers - Avoid simple yes/no or one-word answers - Be unique and not repetitive - Be relevant to real-world use cases Example for Python (intermediate): 1. How would you optimize memory usage in a Python application processing large datasets? 2. Explain the differences between multiprocessing and threading in Python with examples. 3. Describe how you would implement and test a custom context manager in Python. """ response = llm.generate_text(prompt) tech_questions = [] for line in response.split('\n'): line = line.strip() if line and re.match(r'^\d+\.\s*\[.*\]\s*$', line): question = line.split('.', 1)[1].strip()[1:-1].strip() if question: tech_questions.append(f"{tech}: {question}") if len(tech_questions) < 3: default_questions = [ f"{tech}: Explain the most challenging {tech} project you've worked on and the key technical decisions you made.", f"{tech}: How would you optimize performance in a {tech} application handling high traffic?", f"{tech}: Describe your approach to debugging a complex issue in a {tech} application." ] tech_questions.extend(default_questions[:3 - len(tech_questions)]) questions.extend(tech_questions[:3]) return questions[:15] # Limit to 15 questions max to avoid overwhelming candidates