Wire master resume improver prompt to OpenRouter optimizer and add explicit server logs and UI feedback

This commit is contained in:
JobsBoard Deployer 2026-09-05 14:07:45 -04:00
parent e84871926b
commit 62b07080ed
2 changed files with 55 additions and 22 deletions

View file

@ -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;
}
}

View file

@ -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);
}