chatoai-backend / src /extract.js
Keyurjotaniya007's picture
backend at repo root for docker space
0a36812
Raw
History Blame Contribute Delete
2.99 kB
// ----------------------------------------------------------------------------
// Text extraction for uploaded files. Runs server-side where real parsers exist.
// pdf -> pdf-parse
// docx -> mammoth
// html -> html-to-text
// ipynb-> parse notebook JSON (markdown + code + text outputs)
// txt/md/py/json/csv/... -> utf8
// ----------------------------------------------------------------------------
import mammoth from 'mammoth';
import { convert as htmlToText } from 'html-to-text';
import { createRequire } from 'module';
// pdf-parse is CommonJS; import it via createRequire in this ESM module.
const require = createRequire(import.meta.url);
export async function extractText(ext, buffer) {
const e = String(ext || '').toLowerCase().replace(/^\./, '');
try {
if (e === 'pdf') {
// FIX (BUG-2): require the library file directly, NOT the package root.
// pdf-parse's index.js contains a debug block that runs when
// `module.parent` is not set (which is the case under ESM/createRequire)
// and tries to read './test/data/05-versions-space.pdf' — crashing every
// PDF ingest with ENOENT. lib/pdf-parse.js is the real API without that
// debug wrapper.
const pdfParse = require('pdf-parse/lib/pdf-parse.js');
const data = await pdfParse(buffer);
return (data.text || '').trim();
}
if (e === 'docx') {
const { value } = await mammoth.extractRawText({ buffer });
return (value || '').trim();
}
if (e === 'html' || e === 'htm') {
return htmlToText(buffer.toString('utf8'), {
wordwrap: false,
selectors: [
{ selector: 'a', options: { ignoreHref: true } },
{ selector: 'img', format: 'skip' },
],
}).trim();
}
if (e === 'ipynb') {
return extractNotebook(buffer.toString('utf8'));
}
// txt, md, py, json, csv, etc.
return buffer.toString('utf8').trim();
} catch (err) {
console.error(`[extract] ${e} failed:`, err.message);
return '';
}
}
function extractNotebook(json) {
try {
const nb = JSON.parse(json);
const cells = Array.isArray(nb.cells) ? nb.cells : [];
const parts = [];
for (const cell of cells) {
const src = Array.isArray(cell.source) ? cell.source.join('') : cell.source || '';
if (cell.cell_type === 'markdown') {
parts.push(src);
} else if (cell.cell_type === 'code') {
parts.push('```python\n' + src + '\n```');
for (const out of cell.outputs || []) {
const txt =
(out.text && (Array.isArray(out.text) ? out.text.join('') : out.text)) ||
(out.data && out.data['text/plain'] &&
(Array.isArray(out.data['text/plain'])
? out.data['text/plain'].join('')
: out.data['text/plain']));
if (txt) parts.push('Output:\n' + txt);
}
}
}
return parts.join('\n\n').trim();
} catch {
return json;
}
}