From 62b07080edeaa5e7fc7b57166086540649c1a975 Mon Sep 17 00:00:00 2001 From: JobsBoard Deployer Date: Sat, 5 Sep 2026 14:07:45 -0400 Subject: [PATCH] Wire master resume improver prompt to OpenRouter optimizer and add explicit server logs and UI feedback --- web/src/app/api/resume/optimize/route.ts | 67 +++++++++++++++++------- web/src/components/ResumeEditor.tsx | 10 +++- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/web/src/app/api/resume/optimize/route.ts b/web/src/app/api/resume/optimize/route.ts index 83a9028..55980a7 100644 --- a/web/src/app/api/resume/optimize/route.ts +++ b/web/src/app/api/resume/optimize/route.ts @@ -74,7 +74,10 @@ function polishSummaryStrict(currentSummary: string, title?: string): string { // OpenRouter / Small Model Integration (Guaranteed anti-hallucination prompt) async function optimizeWithOpenRouter(payload: { profile: any; work: any[]; skills: string[] }) { const apiKey = process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY; - if (!apiKey) return null; + if (!apiKey) { + console.log("[Resume Optimizer] No OPENROUTER_API_KEY found; applying deterministic local polish."); + return null; + } const isRouter = !!process.env.OPENROUTER_API_KEY; const endpoint = isRouter @@ -84,25 +87,42 @@ async function optimizeWithOpenRouter(payload: { profile: any; work: any[]; skil const model = process.env.OPENROUTER_MODEL || process.env.OPENAI_MODEL || - (isRouter ? "meta-llama/llama-3.2-3b-instruct" : "gpt-4o-mini"); + (isRouter ? "google/gemini-2.5-flash" : "gpt-4o-mini"); - const systemPrompt = `You are an accurate, truth-grounded ATS resume editor. -CRITICAL INTEGRITY INSTRUCTIONS: -1. NEVER invent, fabricate, exaggerate, or assume any facts, tools, employers, degrees, metrics, dates, or certifications. -2. Only rewrite the candidate's existing text to improve grammar, conciseness, and use strong action verbs. -3. Keep the exact same company names, titles, and real duties. -4. If a bullet point is short or simple, polish its grammar; DO NOT add fake percentages, fake dollar amounts, or unmentioned technologies. -5. Return strictly a JSON object with this format: + console.log(`[Resume Optimizer] Invoking LLM via ${endpoint} using model: ${model}`); + + const systemPrompt = `You are a master executive resume optimizer and ATS specialist. +OBJECTIVE: +Transform the candidate's existing summary and work experience bullets into professional, compelling, high-impact statements that pass Workday, Taleo, Greenhouse, and Lever ATS screens. + +CORE RULES: +1. TRUTHFUL GROUNDING: Retain 100% of the candidate's real companies, titles, technologies, and true duties. Do NOT invent new employers or claim experience they do not have. +2. GRAMMATICAL & STYLISTIC UPGRADE: + - Begin every bullet point with a high-impact, past-tense active verb (e.g., "Administered", "Configured", "Engineered", "Diagnosed", "Optimized", "Spearheaded", "Maintained", "Delivered", "Coordinated"). + - Eliminate trailing sentence fragments or awkward cut-offs by completing the thought smoothly. + - Clarify scope, technical context, and impact. +3. OUTPUT FORMAT: +Return strictly valid JSON with this exact schema: { - "profile": { "summary": "polished summary" }, + "profile": { + "summary": "Polished, cohesive professional summary (2-3 sentences)" + }, "work": [ - { "position": "...", "company": "...", "summary": "...", "highlights": ["polished bullet 1", "polished bullet 2"] } + { + "position": "Original or slightly standardized title", + "company": "Company name", + "summary": "Short role summary (or empty string)", + "highlights": [ + "Polished, strong action bullet 1", + "Polished, strong action bullet 2" + ] + } ] }`; try { const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), 12000); + const timeout = setTimeout(() => controller.abort(), 20000); const res = await fetch(endpoint, { method: "POST", @@ -114,13 +134,12 @@ CRITICAL INTEGRITY INSTRUCTIONS: signal: controller.signal, body: JSON.stringify({ model, - temperature: 0.1, - max_tokens: 1800, + temperature: 0.15, messages: [ { role: "system", content: systemPrompt }, { role: "user", - content: `Polish and improve the grammar/action-verbs of this resume while keeping ALL facts 100% true:\n\n${JSON.stringify(payload)}`, + content: `Enhance and polish this resume with strong action verbs and clean syntax while preserving authentic facts:\n\n${JSON.stringify(payload, null, 2)}`, }, ], response_format: { type: "json_object" }, @@ -130,17 +149,25 @@ CRITICAL INTEGRITY INSTRUCTIONS: clearTimeout(timeout); if (!res.ok) { - console.warn("OpenRouter API returned non-200:", res.status, await res.text()); + const errText = await res.text(); + console.error("[Resume Optimizer] LLM API returned non-200:", res.status, errText); return null; } const json = await res.json(); - const rawContent = json.choices?.[0]?.message?.content; - if (!rawContent) return null; + let rawContent = json.choices?.[0]?.message?.content; + if (!rawContent) { + console.warn("[Resume Optimizer] No content returned in LLM choices"); + return null; + } - return JSON.parse(rawContent); + rawContent = rawContent.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim(); + + const parsed = JSON.parse(rawContent); + console.log("[Resume Optimizer] Successfully enhanced resume via LLM"); + return parsed; } catch (err: any) { - console.warn("OpenRouter enhancement skipped or timed out, using deterministic polish:", err.message); + console.error("[Resume Optimizer] LLM call failed or timed out:", err.message); return null; } } diff --git a/web/src/components/ResumeEditor.tsx b/web/src/components/ResumeEditor.tsx index ea46a19..7c5dfda 100644 --- a/web/src/components/ResumeEditor.tsx +++ b/web/src/components/ResumeEditor.tsx @@ -291,12 +291,18 @@ export function ResumeEditor() { if (result.data.profile) setProfile(result.data.profile); if (result.data.work) setWork(result.data.work); if (result.data.skills) setSkills(result.data.skills); - setUploadMessage("Resume bullet points & summary enhanced with high-impact ATS action verbs!"); - setTimeout(() => setUploadMessage(""), 4000); + setUploadMessage("Resume bullet points & summary enhanced with high-impact ATS action verbs."); + setTimeout(() => setUploadMessage(""), 5000); } + } else { + const errJson = await res.json().catch(() => ({})); + setUploadMessage(errJson.error || "Optimization service unavailable. Check API credentials."); + setTimeout(() => setUploadMessage(""), 5000); } } catch (err) { console.error("Optimize error", err); + setUploadMessage("Network error during resume optimization."); + setTimeout(() => setUploadMessage(""), 5000); } finally { setOptimizing(false); }