Ctrl K
ComunidadGuías…

examples/office-documents.mjs

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
// Example: office documents read to text in memory, under bounds on what they inflate to.
//
// Docs: docs/memory/MEMORY_DOCUMENT_INGESTION.md, "PowerPoint, Excel, OpenDocument, RTF and EPUB files".
//
// This script demonstrates:
//   - OfficeLoader from its own entry, @framers/agentos/cognition/memory/office, which imports
//     officeparser on its first read
//   - a document's bytes read under the format its caller names, with the text's word count
//   - the same document read in a worker thread with loadIsolated, as for a file from someone
//     you do not know, under a heap cap and a deadline made for that read
//   - a read cancelled by its own signal, while the loader reads on
//   - a document past a bound refused with DocumentTooLargeError, its message and cause naming the bound
//   - a PDF refused with ScannedDocumentError while OCR is off (ocrPages 0, the default)
//
// With no file named, it writes an OpenDocument text and an EPUB book from Markdown with
// officeparser's converter and reads them. Name files to read those instead:
//   node examples/office-documents.mjs deck.pptx budget.xlsx notes.odt
//
// Usage (no API key, no network; officeparser is an optional peer: npm install officeparser):
//   pnpm install && pnpm run build
//   node examples/office-documents.mjs

import { readFile } from 'node:fs/promises';
import { extname } from 'node:path';
import { OfficeConverter } from 'officeparser';
import {
  DocumentTooLargeError,
  OFFICE_TYPES,
  OfficeLoader,
  ScannedDocumentError,
} from '@framers/agentos/cognition/memory/office';
import { loadIsolated } from '@framers/agentos/cognition/memory';

const loader = new OfficeLoader({
  maxInflatedBytes: 32 * 1024 * 1024,
  maxXmlElements: 400_000,
  maxTableCells: 200_000,
});

/** Prints what a read gave: the word count and the first lines of the text. */
function show(label, doc) {
  const lines = doc.content.split('\n').map((line) => line.trim()).filter(Boolean);
  console.log(`${label}: ${doc.format}, ${doc.metadata.wordCount} words`);
  for (const line of lines.slice(0, 3)) console.log(`  ${line}`);
}

/** A Markdown text written by officeparser's converter as an .odt, .rtf or .epub file. */
async function written(markdown, format) {
  const { value } = await OfficeConverter.convert(Buffer.from(markdown, 'utf8'), format, {
    parseConfig: { fileType: 'md' },
  });
  return typeof value === 'string' ? Buffer.from(value, 'utf8') : Buffer.from(value);
}

const files = process.argv.slice(2);

if (files.length > 0) {
  for (const file of files) {
    const format = OFFICE_TYPES[extname(file).toLowerCase()];
    if (format === undefined) {
      console.log(`${file}: not one of ${Object.keys(OFFICE_TYPES).join(', ')}`);
      continue;
    }
    show(file, await loader.load(await readFile(file), { format, signal: AbortSignal.timeout(30_000) }));
  }
} else {
  const notes = [
    '# Offsite plan',
    '',
    'The rooms are held until the twentieth.',
    '',
    'Travel is booked for the second week of the month.',
  ].join('\n');

  console.log('\n=== an OpenDocument text and an EPUB book, read from their bytes ===\n');
  const odt = await written(notes, 'odt');
  show('notes.odt', await loader.load(odt, { format: 'odt' }));
  show('notes.epub', await loader.load(await written(notes, 'epub'), { format: 'epub' }));

  console.log('\n=== the same text read in a worker thread, as for a file from someone you do not know ===\n');
  show(
    'notes.odt',
    await loadIsolated('odt', odt, { maxHeapMb: 256, maxTableCells: 200_000, signal: AbortSignal.timeout(30_000) }),
  );

  console.log('\n=== a read cancelled by its own signal ===\n');
  const upload = new AbortController();
  upload.abort(new Error('the upload was withdrawn'));
  try {
    await loader.load(odt, { format: 'odt', signal: upload.signal });
    throw new Error('a read whose signal was aborted was read');
  } catch (error) {
    if (error !== upload.signal.reason) throw error;
    console.log(`cancelled: ${error.message}`);
  }
  show('notes.odt, read again with no signal', await loader.load(odt, { format: 'odt' }));

  console.log('\n=== a document past a bound ===\n');
  const long = await written(Array.from({ length: 2_000 }, (_, i) => `Paragraph ${i + 1}.`).join('\n\n'), 'odt');
  const strict = new OfficeLoader({ maxXmlElements: 1_000 });
  try {
    await strict.load(long, { format: 'odt' });
    throw new Error('a document of 2,000 paragraphs was read under a bound of 1,000 XML elements');
  } catch (error) {
    if (!(error instanceof DocumentTooLargeError)) throw error;
    console.log(`refused: ${error.code}, ${error.message} (cause: ${error.cause.message})`);
  }

  console.log('\n=== a PDF while OCR is off ===\n');
  try {
    await loader.load(Buffer.from('%PDF-1.4\n'), { format: 'pdf' });
    throw new Error('a PDF was read with ocrPages at 0');
  } catch (error) {
    if (!(error instanceof ScannedDocumentError)) throw error;
    console.log(`refused: ${error.code}, ${error.message}`);
  }
}