mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-09 20:44:53 +10:00
refactor(ai): collapse parser prompts to template; replace makeEmptyItem with structuredClone
- Replace pdf-parser-system.md + docx-parser-system.md with a single parser-system.md template; prompts.ts substitutes 6 placeholders per source type. Produced strings are byte-identical to the former files. - Remove makeEmptyItem recursive walker (all SECTION_ITEM_SHAPES leaves are already zero-valued) and call structuredClone(shape) instead. Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
This commit is contained in:
@@ -4,11 +4,46 @@ const readPrompt = (filename: string) => {
|
||||
return readFileSync(new URL(`./prompts/${filename}`, import.meta.url), "utf-8");
|
||||
};
|
||||
|
||||
// ponytail: single template with per-source substitutions; produced text is identical per source type
|
||||
const parserTemplate = readPrompt("parser-system.md");
|
||||
|
||||
type ParserVars = {
|
||||
FORMAT_HEADER: string;
|
||||
FORMAT_NOUN: string;
|
||||
ALLOWED_INPUT: string;
|
||||
URL_CLAUSE: string;
|
||||
EXTRA_RULES: string;
|
||||
FALLBACK_CLAUSE: string;
|
||||
};
|
||||
|
||||
function makeParserPrompt(vars: ParserVars): string {
|
||||
return Object.entries(vars).reduce((acc, [k, v]) => acc.replaceAll(`{{${k}}}`, v), parserTemplate);
|
||||
}
|
||||
|
||||
const pdfParserSystemPrompt = makeParserPrompt({
|
||||
FORMAT_HEADER: "PDF files",
|
||||
FORMAT_NOUN: "PDF",
|
||||
ALLOWED_INPUT:
|
||||
"- Use only the visible content from the attached PDF document.\n- Ignore OCR noise, watermarks, repeated headers/footers, and broken line wraps.",
|
||||
URL_CLAUSE: "full URLs that are explicitly present",
|
||||
EXTRA_RULES: "",
|
||||
FALLBACK_CLAUSE: "PDF is low quality or partially unreadable",
|
||||
});
|
||||
|
||||
const docxParserSystemPrompt = makeParserPrompt({
|
||||
FORMAT_HEADER: "Microsoft Word files (DOC/DOCX)",
|
||||
FORMAT_NOUN: "document",
|
||||
ALLOWED_INPUT:
|
||||
"- Use only visible, intended content from the attached document.\n- Ignore hidden text, comments, track changes, revision history, document metadata, and layout artifacts.",
|
||||
URL_CLAUSE: "URLs explicitly visible in document content",
|
||||
EXTRA_RULES:
|
||||
"- Lists and tables: extract visible text faithfully; preserve relationships in section fields.\n- Headers/footers: include only if they contain real resume data.\n",
|
||||
FALLBACK_CLAUSE: "document is malformed or partially unreadable",
|
||||
});
|
||||
|
||||
const analyzeResumeSystemPrompt = readPrompt("analyze-resume-system.md");
|
||||
const chatSystemPromptTemplate = readPrompt("chat-system.md");
|
||||
const docxParserSystemPrompt = readPrompt("docx-parser-system.md");
|
||||
const docxParserUserPrompt = readPrompt("docx-parser-user.md");
|
||||
const pdfParserSystemPrompt = readPrompt("pdf-parser-system.md");
|
||||
const pdfParserUserPrompt = readPrompt("pdf-parser-user.md");
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
You are a strict resume extraction engine for Microsoft Word files (DOC/DOCX). Convert the attached document into a Reactive Resume JSON object.
|
||||
|
||||
## Objective
|
||||
|
||||
- Extract resume content accurately and map it into the provided JSON template.
|
||||
- Prioritize source fidelity and schema correctness over completeness.
|
||||
|
||||
## Allowed Input
|
||||
|
||||
- Use only visible, intended content from the attached document.
|
||||
- Ignore hidden text, comments, track changes, revision history, document metadata, and layout artifacts.
|
||||
|
||||
## Hard Constraints
|
||||
|
||||
1. Extract only explicitly stated information.
|
||||
2. Never fabricate, infer, or normalize missing data.
|
||||
3. Keep original wording and original language.
|
||||
4. When uncertain, omit content and leave template defaults.
|
||||
5. Do not use external knowledge.
|
||||
|
||||
## Conflict Resolution Order
|
||||
|
||||
1. Schema validity (must return valid JSON matching template shape)
|
||||
2. Source fidelity (exactly what the document states)
|
||||
3. Omit uncertain values (never guess)
|
||||
|
||||
## Extraction Rules
|
||||
|
||||
- Dates: preserve exactly as written.
|
||||
- URLs: include only URLs explicitly visible in document content.
|
||||
- Contact data: copy as-is; do not reformat.
|
||||
- Skills: include only explicit skill mentions.
|
||||
- Descriptions: output HTML using `<p>`, `<ul>`, `<li>` while preserving meaning.
|
||||
- Lists and tables: extract visible text faithfully; preserve relationships in section fields.
|
||||
- Headers/footers: include only if they contain real resume data.
|
||||
- IDs: generate unique UUIDs for all `id` fields.
|
||||
- `hidden`: default to `false` unless explicitly indicated otherwise.
|
||||
- `columns`: default to `1` unless clearly multi-column by content intent.
|
||||
- `website`: when missing, use `{ "url": "", "label": "" }`.
|
||||
|
||||
## Section Mapping
|
||||
|
||||
- `basics`, `summary`, `experience`, `education`, `skills`, `projects`, `certifications`, `awards`, `languages`, `volunteer`, `publications`, `references`, `profiles`, `interests`
|
||||
- Map based on explicit headings first; use local context only when heading is absent.
|
||||
|
||||
## Fallback Rules
|
||||
|
||||
- If the document is malformed or partially unreadable, return best-effort extraction for readable parts only.
|
||||
- Keep unknown fields empty according to the template.
|
||||
|
||||
## Output Contract
|
||||
|
||||
- Return only one raw JSON object.
|
||||
- No markdown, no commentary, no extra keys.
|
||||
+6
-7
@@ -1,4 +1,4 @@
|
||||
You are a strict resume extraction engine for PDF files. Convert the attached PDF into a Reactive Resume JSON object.
|
||||
You are a strict resume extraction engine for {{FORMAT_HEADER}}. Convert the attached {{FORMAT_NOUN}} into a Reactive Resume JSON object.
|
||||
|
||||
## Objective
|
||||
|
||||
@@ -7,8 +7,7 @@ You are a strict resume extraction engine for PDF files. Convert the attached PD
|
||||
|
||||
## Allowed Input
|
||||
|
||||
- Use only the visible content from the attached PDF document.
|
||||
- Ignore OCR noise, watermarks, repeated headers/footers, and broken line wraps.
|
||||
{{ALLOWED_INPUT}}
|
||||
|
||||
## Hard Constraints
|
||||
|
||||
@@ -21,17 +20,17 @@ You are a strict resume extraction engine for PDF files. Convert the attached PD
|
||||
## Conflict Resolution Order
|
||||
|
||||
1. Schema validity (must return valid JSON matching template shape)
|
||||
2. Source fidelity (exactly what the PDF states)
|
||||
2. Source fidelity (exactly what the {{FORMAT_NOUN}} states)
|
||||
3. Omit uncertain values (never guess)
|
||||
|
||||
## Extraction Rules
|
||||
|
||||
- Dates: preserve exactly as written.
|
||||
- URLs: include only full URLs that are explicitly present.
|
||||
- URLs: include only {{URL_CLAUSE}}.
|
||||
- Contact data: copy as-is; do not reformat.
|
||||
- Skills: include only explicit skill mentions.
|
||||
- Descriptions: output HTML using `<p>`, `<ul>`, `<li>` while preserving meaning.
|
||||
- IDs: generate unique UUIDs for all `id` fields.
|
||||
{{EXTRA_RULES}}- IDs: generate unique UUIDs for all `id` fields.
|
||||
- `hidden`: default to `false` unless explicitly indicated otherwise.
|
||||
- `columns`: default to `1` unless clearly multi-column by content intent.
|
||||
- `website`: when missing, use `{ "url": "", "label": "" }`.
|
||||
@@ -43,7 +42,7 @@ You are a strict resume extraction engine for PDF files. Convert the attached PD
|
||||
|
||||
## Fallback Rules
|
||||
|
||||
- If the PDF is low quality or partially unreadable, return best-effort extraction for readable parts only.
|
||||
- If the {{FALLBACK_CLAUSE}}, return best-effort extraction for readable parts only.
|
||||
- Keep unknown fields empty according to the template.
|
||||
|
||||
## Output Contract
|
||||
@@ -1,23 +1,5 @@
|
||||
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
|
||||
|
||||
function makeEmptyItem(shape: Record<string, unknown>): Record<string, unknown> {
|
||||
const item: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(shape)) {
|
||||
if (typeof value === "string") item[key] = "";
|
||||
else if (typeof value === "number") item[key] = 0;
|
||||
else if (typeof value === "boolean") item[key] = false;
|
||||
else if (Array.isArray(value)) item[key] = [];
|
||||
else if (value !== null && typeof value === "object") {
|
||||
item[key] = makeEmptyItem(value as Record<string, unknown>);
|
||||
} else {
|
||||
item[key] = "";
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
const SECTION_ITEM_SHAPES: Record<string, Record<string, unknown>> = {
|
||||
profiles: {
|
||||
id: "",
|
||||
@@ -114,7 +96,7 @@ export function buildAiExtractionTemplate() {
|
||||
const sectionKey = key as keyof typeof defaultResumeData.sections;
|
||||
sections[key] = {
|
||||
...defaultResumeData.sections[sectionKey],
|
||||
items: [makeEmptyItem(shape)],
|
||||
items: [structuredClone(shape)],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user