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:
Amruth Pillai
2026-07-04 19:27:13 +02:00
parent dfe75390cd
commit 5762eb6a3e
4 changed files with 44 additions and 82 deletions
+37 -2
View File
@@ -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.
@@ -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 -19
View File
@@ -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)],
};
}