diff --git a/Makefile b/Makefile index fe82973..473f261 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ help: ## Show this help message @echo "" @echo "$(YELLOW)πŸš€ Quick Start:$(NC)" @echo " $(GREEN)make dev-setup$(NC) Set up development environment" - @echo " $(GREEN)make dev-groq$(NC) Fast development with Groq API (recommended)" + @echo " $(GREEN)make dev-groq$(NC) Development with Groq API (recommended)" @echo " $(GREEN)make start$(NC) Full setup with local AI models" @echo "" @echo "$(YELLOW)πŸ“‹ Development Commands:$(NC)" @@ -167,7 +167,7 @@ status: ## Show status of all containers pull-model: ## Download/update the AI model (llama3.2) @echo "$(BLUE)🧠 Downloading llama3.2 model...$(NC)" - @echo "$(YELLOW)This may take a while depending on your internet connection$(NC)" + @echo "$(YELLOW)πŸͺ‘This may take a while depending on your internet connection$(NC)" docker exec $(PROJECT_NAME)-ollama ollama pull llama3.2 @echo "$(GREEN)βœ… Model downloaded successfully$(NC)" @@ -221,8 +221,7 @@ dev-groq: ## Start lightweight development setup (frontend + backend only, optim @echo "$(BLUE)⚑ Starting Groq-optimized development setup...$(NC)" docker compose -f $(DEV_GROQ_COMPOSE) up -d --build --remove-orphans @echo "$(GREEN)βœ… Lightweight development services started with hot reload$(NC)" - @echo "$(YELLOW)Use Groq API in the frontend for fast resume optimization$(NC)" - @echo "$(YELLOW)πŸ’‘ Add your Groq API key in the frontend for ultra-fast optimization$(NC)" + @echo "$(YELLOW)πŸ’‘ Add your Groq API key in the frontend for web based optimization$(NC)" @echo "" @echo "$(BLUE)🌐 Service URLs:$(NC)" @echo "$(GREEN)Frontend: $(NC)http://localhost:3000" @@ -317,7 +316,7 @@ dev-setup: ## Set up local development tools (linting, formatting, pre-commit ho @echo "$(GREEN)βœ… Development tools setup complete!$(NC)" @echo "" @echo "$(BLUE)🎯 Next steps:$(NC)" - @echo " $(GREEN)make dev-groq$(NC) Start development (Groq API - fast)" + @echo " $(GREEN)make dev-groq$(NC) Start development (Groq API)" @echo " $(GREEN)make dev$(NC) Start development (local AI)" @echo " $(GREEN)make lint$(NC) Check code quality" @echo " $(GREEN)make format$(NC) Fix formatting issues" diff --git a/README.md b/README.md index 553a18f..ac0242c 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,40 @@ # ATS-Buddy -Automatically optimizes your resume for specific job listings using local LLM models or Groq via APIs. -[ATS-Buddy API Documentation](http://localhost:8000/docs) +Automatically optimizes your resume for specific job listings using local LLM models or Llama via Groq APIs. -## Features +### Features -- πŸ€– **AI-Powered Optimization**: Uses Ollama for local LLM processing or Groq API for ultra-fast cloud processing -- οΏ½ **Multiple AI Backends**: Choose between local Ollama models or Groq's fast API -- οΏ½πŸ•·οΈ **Web Scraping**: Automatically extracts job descriptions from URLs -- 🎨 **Responsive UI**: Built with Next.js and Tailwind CSS +- 🧬 **Multiple AI Backends**: Uses Ollama for local LLM processing or Groq API for cloud processing +- πŸ•·οΈ **Web Scraping**: Automatically extracts job descriptions from URLs - πŸ”’ **Privacy-Focused**: Local processing option - no data sent to external APIs when using Ollama -- πŸ“± **Multi-Platform**: Works on desktop and mobile devices -- 🎯 **Job Site Support**: Compatible with LinkedIn, Indeed, AngelList, and more ## Architecture -### Frontend (Next.js + TypeScript) +### Frontend (Next.js + Tailwind CSS + TypeScript) - React w/ TypeScript + Tailwind CSS ### Backend (Python + FastAPI) -- FastAPI server for high-performance API +- FastAPI server for API - Ollama integration for local LLM processing - Playwright for dynamic web scraping - BeautifulSoup for HTML parsing +**Application Endpoints:** + +- 🎨 **Frontend**: http://localhost:3000 +- πŸ”§ **Backend API**: http://localhost:8000 +- 🧠 **Ollama**: http://localhost:11434 + ## Prerequisites -Before running this application, make sure you have: +Before running this application outside of docker, make sure you have: 1. **Node.js** (v18 or higher) 2. **Python** (v3.8 or higher) 3. **Ollama** installed and running locally -4. An Ollama model downloaded (e.g., `llama3.2`, `mistral`) +4. An Ollama model downloaded (e.g., `llama3.2`) ### Installing Ollama @@ -41,20 +42,14 @@ Before running this application, make sure you have: 2. Start Ollama service: `ollama serve` 3. Download a model: `ollama pull llama3.2` -### Getting Groq API Key (Optional, for fast web api based processing) +### Getting Groq API Key (Optional, for web api based processing) -For fast resume optimization (~5-10 seconds), you can use Groq's free API: +For faster resume optimization (~5-10 seconds), you can use Groq's free API: 1. Go to [console.groq.com](https://console.groq.com/keys) 2. Sign up for a free account (no credit card required) 3. Generate an API key -4. Use the API key in the frontend interface - -**Groq API Benefits:** - -- ⚑ Faster processing (~5-10 seconds vs ~30-60 seconds locally) -- πŸ†“ Free tier with 6,000 requests per day -- πŸš€ No local GPU/CPU requirements +4. Use the API key in the frontend interface or add it to env.local as `NEXT_PUBLIC_DEFAULT_GROQ_API_KEY` ## Quick Start with Docker (Recommended) @@ -77,63 +72,7 @@ cd ATS-Buddy make quick-start ``` -**Access the application:** - -- 🎨 **Frontend**: http://localhost:3000 -- πŸ”§ **Backend API**: http://localhost:8000 -- 🧠 **Ollama**: http://localhost:11434 - -### Makefile Commands - -```bash -# Basic operations -make start # Start all services -make stop # Stop all services -make restart # Restart all services -make logs # View logs from all services - # Development -make dev # Start in development mode (hot reload) -make dev-rebuild # Rebuild development services - -# Groq API optimized development -make dev-groq # Start lightweight setup (frontend + backend only, optimized for Groq API) -make dev-groq-stop # Stop Groq development services -make dev-groq-logs # View Groq development logs -make dev-groq-rebuild # Rebuild Groq development services - -# Management -make health # Check service health -make status # Show container status -make urls # Show all service URLs - -# AI Model management -make pull-model # Download llama3.2 model -make list-models # List available models - -# Troubleshooting -make clean # Clean everything, keeps build cache -make clean-images # Remove dangling Docker images -make clean-build-cache # Clear Docker build cache -make clobber # Nuclear option: Remove ALL Docker resources -make reset # Full reset and restart - -# Individual service management -make start-ollama # Start only Ollama service -make start-backend # Start only backend service -make start-frontend # Start only frontend service -make rebuild-frontend # Rebuild and restart only frontend - -# Monitoring and utilities -make monitor # Monitor resource usage of containers (full build including ollama container) -make backup-models # Backup AI models to local directory -make urls # Show all service URLs - -# Help and information -make help # Show all available commands -``` - -## Developer Setup For contributors and developers who want to work on ATS-Buddy: @@ -144,20 +83,26 @@ For contributors and developers who want to work on ATS-Buddy: git clone https://github.com/Jared-Krajewski/ATS-Buddy.git cd ATS-Buddy make dev-setup # Install linting tools, pre-commit hooks, dev dependencies -make dev-groq # Start development with Docker (fast) +make dev-groq # Start development with Docker and hot reload ``` This sets up: - **Pre-commit hooks** (automatic code formatting on commit) - **Local linting tools** (for `make lint`, `make format` commands) -- **All dependencies** needed for development workflow +- **All dependencies** needed for development workflows + +Development mode features available with `make dev-groq` via `compose.dev-groq.yml`: + +- πŸ”₯ **Hot reload - Live code changes** for both frontend and backend without rebuilding containers (via volume mounts) +- ⚑ **Faster startup** - No Ollama container needed, uses Groq API for rapidly cloud processed results +- πŸ’» **Lower resource usage** - No local AI model requirements ## Manual Setup (Alternative) If you prefer to run the application **without Docker**, follow these steps: -**Manual setup:** +**Manual setup including Ollama:** ### 1. Set up the Backend @@ -202,8 +147,6 @@ source venv/bin/activate python main.py ``` -The backend will be available at `http://localhost:8000` - ### 5. Start the Frontend ```bash @@ -211,72 +154,60 @@ cd frontend npm run dev ``` -The frontend will be available at `http://localhost:3000` - ---- - -### Mobile Test - -After building the application on development machine.. - -1. To get your local IP, in terminal run.. +## Makefile Commands ```bash -ifconfig | grep "inet " | grep -v 127.0.0.1 | head -1 -``` - -2. Then on a mobile browser naviage to ex:192.168.0.77:3000 - -## App Usage - -Once everything is running, open your browser to **http://localhost:3000** and: - -1. **Enter your resume**: Paste your complete resume text into the left text area -2. **Add job URL**: Enter the URL of the job listing you want to optimize for -3. **Choose AI backend**: Select between Fast Mode (local Ollama) or Groq API (ultra-fast cloud processing) - - **Fast Mode**: Uses smaller local models for quick ~30-60 second results - - **Groq API**: Lightning-fast ~5-10 second results with free API key (6,000 requests/day) -4. **Click "Optimize Resume"**: The AI will analyze the job description and customize your resume -5. **Review results**: See the optimized resume and categorized list of changes made -6. **Copy or download**: Use the optimized resume for your application - -### Supported Job Sites +# Basic operations +make start # Start all services +make stop # Stop all services +make restart # Restart all services +make logs # View logs from all services -- LinkedIn Jobs -- Indeed -- AngelList/Wellfound -- Company career pages -- Most other job listing sites +# Development +make dev # Start in development mode (hot reload) +make dev-rebuild # Rebuild development services -Development mode features available with `make dev` via `compose.dev.yml`: +# Groq API optimized development +make dev-groq # Start lightweight setup (frontend + backend only, optimized for Groq API) +make dev-groq-stop # Stop Groq development services +make dev-groq-logs # View Groq development logs +make dev-groq-rebuild # Rebuild Groq development services -- πŸ”₯ **Hot reload** for both frontend and backend (when using compose.dev.yml) -- πŸ“ **Live code changes** without rebuilding containers (via volume mounts) -- πŸ› **Better debugging** with development configurations +# Management +make health # Check service health +make status # Show container status +make urls # Show all service URLs -### Groq Development Mode +# AI Model management +make pull-model # Download llama3.2 model +make list-models # List available models -For development with Groq API (no Ollama needed): +# Troubleshooting +make clean # Clean everything, keeps build cache +make clean-images # Remove dangling Docker images +make clean-build-cache # Clear Docker build cache +make clobber # Nuclear option: Remove ALL Docker resources +make reset # Full reset and restart -```bash -# Start lightweight development (frontend + backend only) -make dev-groq +# Individual service management +make start-ollama # Start only Ollama service +make start-backend # Start only backend service +make start-frontend # Start only frontend service +make rebuild-frontend # Rebuild and restart only frontend -# View logs -make dev-groq-logs +# Monitoring and utilities +make monitor # Monitor resource usage of containers (full build including ollama container) +make backup-models # Backup AI models to local directory +make urls # Show all service URLs -# Stop services -make dev-groq-stop +# Help and information +make help # Show all available commands ``` -Benefits: - -- ⚑ **Faster startup** - No Ollama container needed -- 🌐 **Cloud processing** - Uses Groq API for faster results -- πŸ’» **Lower resource usage** - No local AI model requirements - ## API Endpoints +with the backend running, visit [ATS-Buddy API Documentation](http://localhost:8000/docs) + ### `GET /health` Health check endpoint that verifies Ollama connectivity and available models. @@ -291,7 +222,6 @@ Main optimization endpoint. { "resume_text": "Your resume content...", "job_url": "https://example.com/job-posting", - "fast_mode": true, "use_groq": false, "groq_api_key": "optional_groq_api_key" } @@ -331,53 +261,49 @@ cp .env.example .env - **API URL**: Update the backend URL in `frontend/src/components/ResumeOptimizer.tsx` -## Development +## Frontend Environment Variables -### Backend Development +The frontend uses environment variables for testing configuration. To set up your development environment: -```bash -cd backend -source venv/bin/activate +1. **Copy the example file:** -# Run with auto-reload -uvicorn main:app --reload --host 0.0.0.0 --port 8000 -``` + ```bash + cd frontend + cp .env.example .env.local + ``` -### Frontend Development +2. **Edit `.env.local` with your values:** -```bash -cd frontend + ```bash + # Default job URL for testing (optional) + NEXT_PUBLIC_DEFAULT_JOB_URL=https://your-test-job-url.com -# Development server with hot reload -npm run dev + # Default Groq API key for testing (optional) + NEXT_PUBLIC_DEFAULT_GROQ_API_KEY=your_groq_api_key_here -# Build for production -npm run build + # Backend API URL + NEXT_PUBLIC_API_URL=http://localhost:8000 + ``` -# Start production server -npm start -``` +### Available Environment Variables -### Code Quality & Pre-commit Hooks +- **`NEXT_PUBLIC_DEFAULT_JOB_URL`** _(optional)_: Pre-fills the job URL field +- **`NEXT_PUBLIC_DEFAULT_GROQ_API_KEY`** _(optional)_: Pre-fills the Groq API key field and enables Groq by default +- **`NEXT_PUBLIC_API_URL`**: Backend API endpoint (defaults to `http://localhost:8000`) -This project uses Husky for automated code quality checks before commits: +### Notes -**First-time setup:** +- The `.env.local` file is ignored by git and won't be committed to the repository +- If `NEXT_PUBLIC_DEFAULT_GROQ_API_KEY` is provided, Groq mode will be enabled by default +- All environment variables are optional - the app will work without them -```bash -# One command to set up everything for development -make dev-setup - -# Or manual setup if you prefer: -npm install # Root dependencies (Husky) -cd frontend && npm install # Frontend dependencies -cd ../backend && python3 -m venv venv && source venv/bin/activate -pip install -r requirements.txt # Backend dependencies + linting tools -playwright install chromium # Browser for web scraping -npx husky install # Set up pre-commit hooks -``` +--- + +### Code Quality & Pre-commit Hooks + +This project uses Husky for automated code quality checks before commits: -**Note**: This sets up local linting tools but you still use Docker to run the application (`make dev-groq`).**Pre-commit checks:** +**Pre-commit checks:** - **Frontend files**: ESLint automatically runs and fixes issues - **Backend files**: Black formatting check @@ -394,7 +320,6 @@ make lint-backend # Check only backend (Black + flake8 + mypy) make format # Auto-fix all formatting issues make lint-fix # Same as make format -# Direct commands # Frontend cd frontend npm run lint # Check and fix TypeScript/JavaScript @@ -406,6 +331,31 @@ python -m black . # Format Python code python -m flake8 . # Check style issues ``` +### Mobile Test + +After building the application on development machine.. + +1. To get your local IP, in terminal run.. + +```bash +ifconfig | grep "inet " | grep -v 127.0.0.1 | head -1 +``` + +2. Then on a mobile browser navigate to ex:192.168.0.77:3000 + +## App Usage + +Once everything is running, open your browser to **http://localhost:3000** and: + +1. **Enter your resume**: Paste your complete resume text into the left text area +2. **Add job URL**: Enter the URL of the job listing you want to optimize for +3. **Choose AI backend**: Select between local Ollama or Groq API (cloud processing) + - **Local Mode**: Uses smaller local models but will have longer processing time (~30-90 seconds) + - **Groq API**: Uses free web API key ( ~5-10 second results, 6,000 requests/day) +4. **Click "Optimize Resume"**: The AI will analyze the job description and customize your resume +5. **Review results**: See the optimized resume and categorized list of changes made +6. **Copy or download**: Use the optimized resume for your application + ## Troubleshooting ### Common Issues @@ -442,24 +392,10 @@ python -m flake8 . # Check style issues 1. Fork the repository 2. Create a feature branch: `git checkout -b feature-name` 3. Make your changes and test them -4. Commit your changes: `git commit -am 'Add feature'` +4. Commit your changes: `git commit -m 'Add feature'` 5. Push to the branch: `git push origin feature-name` 6. Submit a pull request ## License This project is open source and available under the [MIT License](LICENSE). - -## Privacy & Security - -### Local Processing (Ollama) - -- **Local Processing**: All AI processing happens locally using Ollama -- **No Data Collection**: Your resume data never leaves your machine -- **Full Privacy**: No external API calls for sensitive data - -### Cloud Processing (Groq API) - -- **Fast Processing**: Fast results via Groq's API -- **Data Handling**: Resume data is sent to Groq for processing (see Groq's privacy policy) -- **Temporary**: Data is not stored permanently by Groq (Allegedly) diff --git a/backend/main.py b/backend/main.py index 53dfa30..af6a9c2 100644 --- a/backend/main.py +++ b/backend/main.py @@ -44,7 +44,6 @@ class ResumeRequest(BaseModel): resume_text: str job_url: str = None # Make job_url optional job_description: str = None # Add job_description as optional field - fast_mode: bool = True # Enable fast mode by default use_groq: bool = False # Use Groq API instead of Ollama groq_api_key: str = None # Groq API key @@ -155,7 +154,7 @@ async def optimize_resume_endpoint(request: ResumeRequest): Main endpoint to optimize a resume based on a job listing URL or provided job description. """ try: - logger.info(f"Received request to optimize resume (fast_mode: {request.fast_mode}, use_groq: {request.use_groq})") + logger.info(f"Received request to optimize resume (use_groq: {request.use_groq})") # Step 0: validation: # Validate that either job_url or job_description is provided @@ -172,15 +171,14 @@ async def optimize_resume_endpoint(request: ResumeRequest): job_description = request.job_description logger.info("Using provided job description") else: - # Scrape job description from URL with fast mode - job_description = await scrape_job_listing(request.job_url, use_fast_mode=request.fast_mode) + # Scrape job description from URL + job_description = await scrape_job_listing(request.job_url) logger.info("Successfully scraped job description") - # Step 2: Optimize resume using LLM with fast mode or Groq + # Step 2: Optimize resume using LLM with Groq result = await optimize_resume( request.resume_text, job_description, - use_fast_mode=request.fast_mode, use_groq=request.use_groq, groq_api_key=request.groq_api_key, ) diff --git a/backend/resume_optimizer.py b/backend/resume_optimizer.py index 7be0bc8..dfc51b3 100644 --- a/backend/resume_optimizer.py +++ b/backend/resume_optimizer.py @@ -10,9 +10,8 @@ logger = logging.getLogger(__name__) -# Use a smaller, faster model by default, toggled by fast_mode -DEFAULT_MODEL = "llama3.2:1b" # Smaller 1B parameter model for speed -FALLBACK_MODEL = "llama3.2" # Fallback to full model if needed +DEFAULT_MODEL = "llama3.2" # Full 70b parameter model +FALLBACK_MODEL = "llama3.2:1b" def clean_resume_text(text: str) -> str: @@ -68,7 +67,6 @@ async def optimize_resume( resume_text: str, job_description: str, model: str = None, - use_fast_mode: bool = True, use_groq: bool = False, groq_api_key: str = None, ) -> Dict[str, any]: @@ -77,19 +75,17 @@ async def optimize_resume( """ try: if use_groq: - # Use Groq API for fast optimization - return await optimize_resume_with_groq(resume_text, job_description, groq_api_key, use_fast_mode) + # Use Groq API + return await optimize_resume_with_groq(resume_text, job_description, groq_api_key) else: # Use local Ollama - return await optimize_resume_with_ollama(resume_text, job_description, model, use_fast_mode) + return await optimize_resume_with_ollama(resume_text, job_description, model) except Exception as e: logger.error(f"Error optimizing resume: {e}") raise Exception(f"Failed to optimize resume: {str(e)}") -async def optimize_resume_with_groq( - resume_text: str, job_description: str, api_key: str, use_fast_mode: bool = True -) -> Dict[str, any]: +async def optimize_resume_with_groq(resume_text: str, job_description: str, api_key: str) -> Dict[str, any]: """ Optimize resume using Groq API for ultra-fast processing """ @@ -97,11 +93,9 @@ async def optimize_resume_with_groq( if not api_key: raise Exception("Groq API key is required") - # model = "llama-3.1-8b-instant" #use fast model to avoid token limits - model = "llama-3.3-70b-versatile" - # Smart truncation to stay within Groq's 12K token limit + # Smart truncation to stay within Groq's 6K token limit # Estimate: ~4 chars per token, leave room for system prompt and response max_total_chars = 8000 # Conservative limit for combined input @@ -121,8 +115,8 @@ async def optimize_resume_with_groq( truncated_resume = resume_text truncated_job = job_description - # Always use fast optimization for Groq - prompt = create_fast_optimization_prompt(truncated_resume, truncated_job) + # Always use optimization for Groq + prompt = create_optimization_prompt(truncated_resume, truncated_job) # Make request to Groq API async with httpx.AsyncClient() as client: @@ -138,8 +132,8 @@ async def optimize_resume_with_groq( }, {"role": "user", "content": prompt}, ], - "temperature": 0.3 if use_fast_mode else 0.7, - # "max__completion_tokens": 1000 if use_fast_mode else 2000, # add this back in if responses are too long. I dont think the free plan limits my response length currently. + "temperature": 0.3, + # "max__completion_tokens": 1000, # add this back in if responses are too long. I dont think the free plan limits my response length currently. }, timeout=30, ) @@ -163,9 +157,7 @@ async def optimize_resume_with_groq( raise Exception(f"Failed to optimize resume with Groq: {str(e)}") -async def optimize_resume_with_ollama( - resume_text: str, job_description: str, model: str = None, use_fast_mode: bool = True -) -> Dict[str, any]: +async def optimize_resume_with_ollama(resume_text: str, job_description: str, model: str = None) -> Dict[str, any]: """ Optimize resume using local Ollama """ @@ -180,10 +172,10 @@ async def optimize_resume_with_ollama( if not model_names: raise Exception("No Ollama models available. Please install a model first.") - # Choose model based on mode and availability + # Choose model based on availability - prefer smaller, faster model if model is None: - # Define preferred model based on mode - preferred_models = [DEFAULT_MODEL, FALLBACK_MODEL] if use_fast_mode else [FALLBACK_MODEL, DEFAULT_MODEL] + # Use fast model by default + preferred_models = [DEFAULT_MODEL, FALLBACK_MODEL] # Use first available preferred model, or fallback to any available model model = next((model for model in preferred_models if model in model_names), model_names[0]) @@ -192,11 +184,8 @@ async def optimize_resume_with_ollama( model = model_names[0] logger.info(f"Requested model not found, using: {model}") - # Create optimized prompt based on mode - if use_fast_mode: - prompt = create_fast_optimization_prompt(resume_text, job_description) - else: - prompt = create_optimization_prompt(resume_text, job_description) + # Create optimized prompt + prompt = create_optimization_prompt(resume_text, job_description) # Generate optimized resume using Ollama with performance settings response = ollama_client.chat( @@ -209,9 +198,9 @@ async def optimize_resume_with_ollama( {"role": "user", "content": prompt}, ], options={ - "temperature": 0.3 if use_fast_mode else 0.7, # Lower temperature for speed - "top_p": 0.8 if use_fast_mode else 0.9, - "num_predict": 1000 if use_fast_mode else 2000, # Limit response length for speed + "temperature": 0.3, # Use lower temperature for more consistent results + "top_p": 0.8, + "num_predict": 1000, # Limit response length for speed "num_ctx": 4096, # Reasonable context window }, ) @@ -219,7 +208,7 @@ async def optimize_resume_with_ollama( # Parse the response result = parse_ollama_response(response["message"]["content"]) - logger.info(f"Successfully optimized resume using {model} in {'fast' if use_fast_mode else 'full'} mode") + logger.info(f"Successfully optimized resume using {model}") return result except Exception as e: @@ -227,9 +216,9 @@ async def optimize_resume_with_ollama( raise Exception(f"Failed to optimize resume with Ollama: {str(e)}") -def create_fast_optimization_prompt(resume_text: str, job_description: str) -> str: +def create_optimization_prompt(resume_text: str, job_description: str) -> str: """ - Create a format-preserving prompt for PDF optimization with strict constraints + Create a format-preserving prompt for resume optimization with strict constraints """ # Only truncate if really necessary to prevent token overflow resume_preview = resume_text[:5000] + ("..." if len(resume_text) > 5000 else "") @@ -266,53 +255,6 @@ def create_fast_optimization_prompt(resume_text: str, job_description: str) -> s REMEMBER: Same order, same structure, similar length, better keywords.""" -def create_optimization_prompt(resume_text: str, job_description: str) -> str: - """Create a format-preserving detailed prompt for resume optimization""" - return f"""OPTIMIZE this resume for the job description with STRICT FORMAT PRESERVATION: - -**CRITICAL CONSTRAINTS - MUST FOLLOW:** -1. **MAINTAIN EXACT STRUCTURE**: Keep all sections in the same order and format -2. **NO NEW SECTIONS**: Only modify existing content, never add new sections or bullet points -3. **LENGTH PRESERVATION**: Keep text within Β±20% of original length per section -4. **NO REORDERING**: Do not rearrange sections, experiences, or bullet points -5. **TRUTHFULNESS**: Only modify language and emphasis, never add false information - -**FORMATTING REQUIREMENTS:** -- Preserve all line breaks and paragraph structure exactly -- Keep the same number of bullet points per section -- Maintain original spacing and organization -- Only improve wording within existing structure - -**Original Resume:** -{resume_text} - -**Job Description:** -{job_description} - -**Allowed Optimizations:** -- Replace outdated technologies with relevant ones mentioned in job description -- Strengthen action verbs and add quantifiable metrics -- Naturally incorporate relevant keywords into existing sentences -- Improve clarity and impact of existing bullet points -- Align existing achievements with job requirements - -CRITICAL: Return ONLY valid JSON. Do NOT include any extra text outside the JSON structure. - -{{ - "optimized_resume": "COMPLETE RESUME TEXT PRESERVING EXACT STRUCTURE", - "changes_made": [ - "Specific description of change 1", - "Specific description of change 2" - ], - "keyword_matches": [ - "keyword1", - "keyword2" - ] -}} - -IMPORTANT: The optimized_resume must have the same structure, sections, and approximate length as the original. Only the content quality should improve.""" - - def parse_ollama_response(response_text: str) -> Dict[str, any]: """ Parse Ollama/Groq response and extract structured data diff --git a/backend/scraper.py b/backend/scraper.py index e92efd5..8823bff 100644 --- a/backend/scraper.py +++ b/backend/scraper.py @@ -9,29 +9,20 @@ logger = logging.getLogger(__name__) -async def scrape_job_listing(url: str, use_fast_mode: bool = True) -> str: +async def scrape_job_listing(url: str) -> str: """ Scrape job description from a given URL with optimized performance. - Fast mode prioritizes speed over completeness. + Uses requests first for speed, then fallbacks to Playwright for dynamic content. """ try: - job_description = None - - if use_fast_mode: - # Fast mode: Try requests first, fallback to Playwright only if needed - logger.info("Using fast mode - trying requests first") - job_description = await scrape_with_requests(url) - - # Only use Playwright if requests fails and for known dynamic sites - if not job_description and requires_javascript(url): - logger.info("Fallback to Playwright for dynamic content") - job_description = await scrape_with_playwright(url, timeout=15000) # Reduced timeout - else: - # Full mode: Use Playwright first for maximum compatibility - job_description = await scrape_with_playwright(url, timeout=30000) - - if not job_description: - job_description = await scrape_with_requests(url) + # Try requests first for speed + logger.info("Attempting to scrape with requests first") + job_description = await scrape_with_requests(url) + + # Only use Playwright if requests fails and for known dynamic sites + if not job_description and requires_javascript(url): + logger.info("Fallback to Playwright for dynamic content") + job_description = await scrape_with_playwright(url, timeout=15000) if job_description: return job_description.strip() diff --git a/frontend/ENV_SETUP.md b/frontend/ENV_SETUP.md deleted file mode 100644 index 93ae379..0000000 --- a/frontend/ENV_SETUP.md +++ /dev/null @@ -1,37 +0,0 @@ -# Environment Setup - -## Frontend Environment Variables - -The frontend uses environment variables for testing configuration. To set up your development environment: - -1. **Copy the example file:** - - ```bash - cd frontend - cp .env.example .env.local - ``` - -2. **Edit `.env.local` with your values:** - - ```bash - # Default job URL for testing (optional) - NEXT_PUBLIC_DEFAULT_JOB_URL=https://your-test-job-url.com - - # Default Groq API key for testing (optional) - NEXT_PUBLIC_DEFAULT_GROQ_API_KEY=your_groq_api_key_here - - # Backend API URL - NEXT_PUBLIC_API_URL=http://localhost:8000 - ``` - -## Available Environment Variables - -- **`NEXT_PUBLIC_DEFAULT_JOB_URL`** _(optional)_: Pre-fills the job URL field -- **`NEXT_PUBLIC_DEFAULT_GROQ_API_KEY`** _(optional)_: Pre-fills the Groq API key field and enables Groq by default -- **`NEXT_PUBLIC_API_URL`**: Backend API endpoint (defaults to `http://localhost:8000`) - -## Notes - -- The `.env.local` file is ignored by git and won't be committed to the repository -- If `NEXT_PUBLIC_DEFAULT_GROQ_API_KEY` is provided, Groq mode will be enabled by default -- All environment variables are optional - the app will work without them diff --git a/frontend/src/components/ResumeOptimizer.tsx b/frontend/src/components/ResumeOptimizer.tsx index ece3a89..ca8a149 100644 --- a/frontend/src/components/ResumeOptimizer.tsx +++ b/frontend/src/components/ResumeOptimizer.tsx @@ -1,26 +1,26 @@ "use client"; -import { useState } from "react"; import { AlertCircle, + Award, + BookOpen, + Briefcase, CheckCircle, - Loader2, - Copy, - Download, ChevronDown, ChevronRight, - Zap, - Target, - Users, - Award, Code, - Briefcase, - BookOpen, - Plus, + Copy, + Download, Edit3, - Upload, FileText, + Loader2, + Plus, + Target, + Upload, + Users, + Zap, } from "lucide-react"; +import { useState } from "react"; interface OptimizationResult { optimized_resume: string; @@ -642,7 +642,7 @@ export function ResumeOptimizer() { className="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" /> - Fast Mode (~30-60 seconds) - Local AI + Ollama (~30-90 seconds) - Local AI

@@ -666,7 +666,7 @@ export function ResumeOptimizer() { className="rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" /> - Use Groq API (~5-10 seconds) - Ultra Fast ⚑ + Use Groq (~5-10 seconds) - Web API ⚑