|
| 1 | +import { readFileSync } from 'fs'; |
| 2 | + |
| 3 | +const envContent = readFileSync('.env', 'utf-8'); |
| 4 | +const GEMINI_KEY = envContent.match(/GEMINI_API_KEY=(.+)/)?.[1]?.trim(); |
| 5 | + |
| 6 | +if (!GEMINI_KEY) { |
| 7 | + console.error('no GEMINI_API_KEY in .env'); |
| 8 | + process.exit(1); |
| 9 | +} |
| 10 | + |
| 11 | +// simulate the real scoring prompt (similar size to buildFullScoringPrompt) |
| 12 | +const resumeText = |
| 13 | + 'Sunny Patel. (437) 216-1611. Software Engineer Intern at IBM. IT Technician at Canadas Wonderland. Supported tenant-to-tenant migration during Six Flags acquisition for 3000+ directory objects. Authored 10+ PowerShell/ConnectWise scripts automating workstation imaging. Built and deployed MDT task sequences. Managed Active Directory accounts, security groups, and GPOs. System Support Specialist at Mackenzie Health. Migrated 400+ Surface tablets to bedside iPads. Skills: Java, Python, Go, Scala, PowerShell, C++, C#, YAML, Kotlin, Assembly, Django, Ruby on Rails, MongoDB, PostgreSQL, MySQL, Express.js, ASP.NET Core, Spring Boot, Kafka, React.js, JavaScript, Flutter, TypeScript, WebGL, GraphQL, Tailwind CSS, Three.js, Vue.js, Git, Docker, Kubernetes, Azure, GCP, AWS, Jamf Pro, Datadog. Education: Ontario Tech University, Honours BSc Computer Science. Projects: Axelot collaborative document platform with Next.js 16 and WebRTC, Netdash Electron networking toolkit with 15+ tools, SecureBank CTF banking app for SQL injection training, Sunnify Spotify downloader with PyQt5. Certifications: Microsoft GH-300 GitHub Copilot Intermediate, MongoDB Python Developer Path, GitHub Foundations, ConnectWise Automate Certified Enterprise Scripting Architect, Google IT Automation with Python.'; |
| 14 | + |
| 15 | +const scoringPrompt = `You are a senior talent acquisition technology analyst. Analyze this resume from the perspective of 6 enterprise ATS platforms. |
| 16 | +
|
| 17 | +<RESUME> |
| 18 | +${resumeText} |
| 19 | +</RESUME> |
| 20 | +
|
| 21 | +MODE: general ATS readiness. Evaluate formatting, structure, and keyword density. |
| 22 | +
|
| 23 | +## PLATFORM SPECIFICATIONS |
| 24 | +### 1. WORKDAY RECRUITING - strict parser, skips headers/footers, penalizes creative formats |
| 25 | +### 2. ORACLE TALEO - literal exact keyword match, strictest matching |
| 26 | +### 3. iCIMS - semantic ML-based matching, most forgiving parser |
| 27 | +### 4. GREENHOUSE - LLM-based parser, no auto-scoring, human review focused |
| 28 | +### 5. LEVER - stemming-based matching, no ranking system |
| 29 | +### 6. SAP SUCCESSFACTORS - Textkernel parser, taxonomy normalization |
| 30 | +
|
| 31 | +Score each platform on: formatting (0-100), keywordMatch (0-100), sections (0-100), experience (0-100), education (0-100), overallScore (0-100). |
| 32 | +
|
| 33 | +Respond ONLY with valid JSON matching this structure: |
| 34 | +{ |
| 35 | + "results": [ |
| 36 | + { |
| 37 | + "system": "Workday", |
| 38 | + "vendor": "Workday Inc.", |
| 39 | + "overallScore": 75, |
| 40 | + "passesFilter": true, |
| 41 | + "breakdown": { |
| 42 | + "formatting": { "score": 80, "issues": [], "details": [] }, |
| 43 | + "keywordMatch": { "score": 70, "matched": [], "missing": [], "synonymMatched": [] }, |
| 44 | + "sections": { "score": 85, "present": [], "missing": [] }, |
| 45 | + "experience": { "score": 75, "quantifiedBullets": 5, "totalBullets": 10, "actionVerbCount": 7, "highlights": [] }, |
| 46 | + "education": { "score": 90, "notes": [] } |
| 47 | + }, |
| 48 | + "suggestions": [] |
| 49 | + } |
| 50 | + ] |
| 51 | +} |
| 52 | +
|
| 53 | +Return exactly 6 results: Workday, Taleo, iCIMS, Greenhouse, Lever, SuccessFactors.`; |
| 54 | + |
| 55 | +console.log('prompt length:', scoringPrompt.length, 'chars'); |
| 56 | +console.log('estimated tokens:', Math.ceil(scoringPrompt.length / 3.5)); |
| 57 | +console.log(''); |
| 58 | + |
| 59 | +async function test() { |
| 60 | + const start = Date.now(); |
| 61 | + const res = await fetch( |
| 62 | + `https://generativelanguage.googleapis.com/v1beta/models/gemma-3-27b-it:generateContent?key=${GEMINI_KEY}`, |
| 63 | + { |
| 64 | + method: 'POST', |
| 65 | + headers: { 'Content-Type': 'application/json' }, |
| 66 | + body: JSON.stringify({ |
| 67 | + contents: [{ parts: [{ text: scoringPrompt }] }], |
| 68 | + generationConfig: { temperature: 0.3, topP: 0.85, maxOutputTokens: 16384 } |
| 69 | + }) |
| 70 | + } |
| 71 | + ); |
| 72 | + |
| 73 | + const elapsed = Date.now() - start; |
| 74 | + console.log('status:', res.status, `(${elapsed}ms)`); |
| 75 | + |
| 76 | + if (!res.ok) { |
| 77 | + const err = await res.text(); |
| 78 | + console.log('ERROR:', err.slice(0, 500)); |
| 79 | + return; |
| 80 | + } |
| 81 | + |
| 82 | + const data = await res.json(); |
| 83 | + const text = data.candidates?.[0]?.content?.parts?.[0]?.text ?? ''; |
| 84 | + console.log('response length:', text.length, 'chars'); |
| 85 | + |
| 86 | + // try JSON parse (same logic as extractJSON in +server.ts) |
| 87 | + const trimmed = text.trim(); |
| 88 | + |
| 89 | + // attempt 1: direct parse |
| 90 | + try { |
| 91 | + JSON.parse(trimmed); |
| 92 | + console.log('JSON parse: DIRECT SUCCESS'); |
| 93 | + return; |
| 94 | + } catch { |
| 95 | + /* continue */ |
| 96 | + } |
| 97 | + |
| 98 | + // attempt 2: strip markdown fences |
| 99 | + const cleaned = trimmed.replace(/```json\n?|\n?```/g, '').trim(); |
| 100 | + try { |
| 101 | + JSON.parse(cleaned); |
| 102 | + console.log('JSON parse: SUCCESS (after fence strip)'); |
| 103 | + return; |
| 104 | + } catch { |
| 105 | + /* continue */ |
| 106 | + } |
| 107 | + |
| 108 | + // attempt 3: find { ... } block |
| 109 | + const s = cleaned.indexOf('{'); |
| 110 | + const e = cleaned.lastIndexOf('}'); |
| 111 | + if (s !== -1 && e > s) { |
| 112 | + try { |
| 113 | + JSON.parse(cleaned.slice(s, e + 1)); |
| 114 | + console.log('JSON parse: SUCCESS (extracted { } block)'); |
| 115 | + return; |
| 116 | + } catch { |
| 117 | + /* continue */ |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + console.log('JSON parse: FAILED - this is why Gemma falls through to Groq'); |
| 122 | + console.log('--- first 500 chars of response ---'); |
| 123 | + console.log(text.slice(0, 500)); |
| 124 | + console.log('--- last 200 chars of response ---'); |
| 125 | + console.log(text.slice(-200)); |
| 126 | +} |
| 127 | + |
| 128 | +test().catch(console.error); |
0 commit comments