import fs from 'node:fs/promises'; import path from 'node:path'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); /* * Text aus einem Lebenslauf holen. * * Unterstuetzt PDF, DOCX und reinen Text. Das alte .doc-Format (Word 97) bleibt * aussen vor: es braeuchte einen eigenen Parser, und wer eine solche Datei hat, * speichert sie in Sekunden als PDF. * * pdf-parse wird ueber den internen Pfad geladen. Der Paket-Einstieg fuehrt * beim Import eine Selbstprobe aus und sucht dafuer eine Testdatei, die im * npm-Paket nicht mitgeliefert wird - das laesst den Start scheitern. */ const pdfParse = require('pdf-parse/lib/pdf-parse.js'); /** Mehr Text braucht kein Modell, um einen Lebenslauf zu verstehen. */ const MAX_CHARS = 60_000; export function isSupported(filename, mime = '') { const ext = path.extname(filename).toLowerCase(); return ( ['.pdf', '.docx', '.txt', '.md'].includes(ext) || mime === 'application/pdf' || mime === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' || mime.startsWith('text/') ); } /** * Liest die Datei und gibt ihren Text zurueck. * * @param {string} filePath absoluter Pfad * @param {string} originalName fuer die Endung */ export async function extractText(filePath, originalName) { const ext = path.extname(originalName || filePath).toLowerCase(); if (ext === '.pdf') { const result = await pdfParse(await fs.readFile(filePath)); return clean(result.text); } if (ext === '.docx') { // mammoth liefert den Fliesstext ohne Formatierung - genau das, was das // Modell braucht. const mammoth = require('mammoth'); const result = await mammoth.extractRawText({ path: filePath }); return clean(result.value); } if (ext === '.txt' || ext === '.md') { return clean(await fs.readFile(filePath, 'utf8')); } throw Object.assign(new Error('Dateityp wird nicht unterstützt (PDF, DOCX oder Text).'), { status: 400, }); } /* * Aufraeumen: PDF-Auszuege enthalten viele harte Zeilenumbrueche und * Mehrfachleerzeichen aus dem Satzspiegel. Das kostet nur Token und erschwert * dem Modell das Lesen. */ function clean(text) { const result = String(text ?? '') .replace(/\r/g, '') .replace(/[ \t]+/g, ' ') .replace(/\n{3,}/g, '\n\n') .trim(); if (result.length <= MAX_CHARS) return result; // Anfang und Ende behalten: vorn stehen Name und Kontaktdaten, hinten oft // Ausbildung und Zertifikate. const head = result.slice(0, Math.floor(MAX_CHARS * 0.7)); const tail = result.slice(-Math.floor(MAX_CHARS * 0.3)); return `${head}\n\n[… gekürzt …]\n\n${tail}`; }