Skip to main content
Version: 3.1

Using Document Intelligence

Document Intelligence converts PDF, Word, and Excel documents and web pages into Markdown so that your Reaktors can process their content with AI models. Under the hood it uses Azure Document Intelligence (formerly Form Recognizer) for PDF conversion, mammoth for Word, SheetJS for Excel, and a built-in HTML-to-Markdown transformer for web pages. Everything is available through the @operaide/document package.


Overview​

The package provides six groups of Aktors:

GroupPurpose
PDF ConversionConvert PDFs (URL or base64) to Markdown
PDF Text ExtractionExtract raw text from PDFs (URL or base64), no Azure connection required
Word ConversionConvert DOCX files (base64) to Markdown
Excel ConversionConvert XLSX workbooks (base64) to one CSV block per sheet
Page & Chapter ManipulationSplit, filter, and number pages and chapters within converted Markdown
URL to MarkdownFetch web pages and convert their HTML to Markdown

PDF conversion requires a configured Connection of type document-intelligence. See the administrator guide for connection setup, caching, and proxy configuration. For an alternative PDF backend, see Using Mistral OCR.


PDF Conversion​

aktorConvertPDFToMarkdown​

Converts a single PDF to Markdown. The PDF can be passed as a URL or a base64 data string.

import { aktorConvertPDFToMarkdown } from '@operaide/document';

const markdown = aktorConvertPDFToMarkdown({
pdf: pdfUrlOrBase64, // URL string or base64 data
connectionName: 'default', // optional — name of the document-intelligence connection
});
ParameterTypeDescription
pdfstringThe PDF to convert — either a URL or a data:application/pdf;base64,… string
connectionNamestring (optional)Name of the Document Intelligence connection. Falls back to system default when omitted. Use 'default' for the default connection.

Returns a string containing the Markdown representation of the PDF.

aktorConvertMultiplePDFsToMarkdown​

Batch-converts an array of PDFs. Each PDF is converted sequentially through the same connection.

import { aktorConvertMultiplePDFsToMarkdown } from '@operaide/document';

const markdowns = aktorConvertMultiplePDFsToMarkdown({
pdfs: [pdfUrl1, pdfUrl2],
connectionName: 'default',
});
ParameterTypeDescription
pdfsstring[]Array of PDF URLs or base64 strings
connectionNamestring (optional)Connection name (same as above)

Returns string[] — one Markdown string per input PDF.

Full Example: PDF to AI Extraction​

A typical workflow converts a PDF attachment to Markdown and then passes the text to an LLM for structured extraction:

import { aktorSetting, createAktorComposition, registerReaktorDefinition } from '@operaide/aktor';
import { aktorAICall, aktorAISettingProviderModel, aktorMessagesFromSystemAndUser } from '@operaide/ai';
import { aktorConvertPDFToMarkdown } from '@operaide/document';
import { z } from 'zod';

const aktorInvoiceExtractor = createAktorComposition('aktorInvoiceExtractor', ({ pdf }) => {
const connectionName = aktorSetting(
z.string(),
'default',
'Document Intelligence Connection'
);

const markdown = aktorConvertPDFToMarkdown({ pdf, connectionName });

return aktorAICall({
messages: aktorMessagesFromSystemAndUser({
system: aktorSetting(
z.string().describe('[textarea] System Prompt'),
'Extract invoice number, date, total, and line items from the document.',
'System Prompt'
),
user: markdown,
}),
providerModel: aktorAISettingProviderModel(),
});
});

registerReaktorDefinition({
reaktorDefinitionId: 'invoice-extractor',
label: 'Invoice Extractor',
description: 'Extract structured data from a PDF invoice',
inputSchema: z.object({
pdf: z.string().describe('[file-upload] PDF invoice'),
}),
outputSchema: z.string(),
aktor: aktorInvoiceExtractor,
});
tip

Wrap connectionName in aktorSetting so that operators can switch the connection at deployment time without code changes.


Word Conversion​

Word conversion runs locally with mammoth and turndown. No connection required.

aktorConvertDocxToMarkdown​

Converts a single DOCX file to Markdown.

import { aktorConvertDocxToMarkdown } from '@operaide/document';

const markdown = aktorConvertDocxToMarkdown({
docx: docxBase64, // data:...;base64,... or raw base64
});
ParameterTypeDescription
docxstringDOCX file as base64 data, optionally with a data: URI prefix

Returns a string containing GFM Markdown. Headings, lists, and tables are preserved.

aktorConvertMultipleDocxsToMarkdown​

Batch-converts an array of DOCX files.

import { aktorConvertMultipleDocxsToMarkdown } from '@operaide/document';

const markdowns = aktorConvertMultipleDocxsToMarkdown({
docxs: [docx1, docx2],
});
ParameterTypeDescription
docxsstring[]Array of DOCX base64 strings

Returns string[] — one Markdown string per input file.


Excel Conversion​

Excel conversion runs locally with SheetJS. No connection required. Each sheet becomes a fenced csv block under a ## Sheet: <name> heading, so an LLM can read both the structure and the data.

aktorConvertXlsxToCsv​

Converts a single XLSX workbook to Markdown.

import { aktorConvertXlsxToCsv } from '@operaide/document';

const markdown = aktorConvertXlsxToCsv({
xlsx: xlsxBase64,
});
ParameterTypeDescription
xlsxstringXLSX file as base64 data, optionally with a data: URI prefix

Returns a string. Empty sheets are skipped.

Example output:

## Sheet: Customers

```csv
id,name,email
1,Alice,alice@example.com
2,Bob,bob@example.com
```

## Sheet: Orders

```csv
id,customer_id,total
101,1,49.99
```

aktorConvertMultipleXlsxsToCsv​

Batch-converts an array of XLSX workbooks.

import { aktorConvertMultipleXlsxsToCsv } from '@operaide/document';

const markdowns = aktorConvertMultipleXlsxsToCsv({
xlsxs: [xlsx1, xlsx2],
});
ParameterTypeDescription
xlsxsstring[]Array of XLSX base64 strings

Returns string[] — one Markdown document per workbook.


Working with Converted Markdown​

Azure Document Intelligence embeds page markers into the Markdown output. The package provides Aktors that operate on these markers to split, filter, and serialize page content.

Page Markers​

The converted Markdown contains two types of HTML comments:

  • <!-- PageBreak --> — separates pages
  • <!-- PageNumber="5" --> — indicates the detected page number (numeric or Roman numeral)

Key Types​

interface PageData {
content: string; // Page content including markers
pageNumbers: string[]; // All detected page numbers on this page
pageNumber: string; // Assigned page number (numeric or Roman)
}

interface PageListEntry {
documentId: string;
pages: PageData[];
}

interface VectorDbDocumentMarkdown {
documentId: string;
markdown: string;
}

aktorSplitMarkdownToPages​

Splits an array of VectorDbDocumentMarkdown objects into PageListEntry objects. Each document's Markdown is split at <!-- PageBreak --> markers and page numbers are extracted.

import { aktorSplitMarkdownToPages } from '@operaide/document';

const pageList = aktorSplitMarkdownToPages({
markdown: [
{ documentId: 'doc-1', markdown: convertedMarkdown },
],
});
// pageList: PageListEntry[]

aktorGetFirstPages​

Returns the first N pages of each document as a JSON string. Defaults to 3 pages when pageCount is omitted.

import { aktorGetFirstPages } from '@operaide/document';

const firstPagesJson = aktorGetFirstPages({
documents: pageList,
pageCount: 2,
});

aktorGetContentFromPages​

Filters documents to only include specific pages. Pass a DocumentPages[] array where each entry maps a document ID to a comma-separated list of page numbers. If pages is 'null', all pages of that document are returned.

import { aktorGetContentFromPages } from '@operaide/document';

const filtered = aktorGetContentFromPages({
relevantPages: [
{ documentId: 'doc-1', pages: '1, 3, 5' },
],
documents: pageList,
});

aktorGetJSONFromDocuments​

Serializes a PageListEntry[] array to a JSON string.

import { aktorGetJSONFromDocuments } from '@operaide/document';

const json = aktorGetJSONFromDocuments({ documents: pageList });

Chapter Extraction​

These Aktors add chapter numbering to Markdown headings and extract a table of contents. They are useful when you need an LLM to work with specific sections of a long document.

aktorGetChaptersFromMarkdown​

Takes an array of Markdown strings, adds chapter numbers to headings (e.g. 1, 1.1, 2), and returns the table of contents as a string.

import { aktorGetChaptersFromMarkdown } from '@operaide/document';

const toc = aktorGetChaptersFromMarkdown({
documents: [markdownString1, markdownString2],
});
// "# 1 Introduction\n## 1.1 Background\n# 2 Methods\n…"

aktorMarkdownChapterNumbers​

Adds chapter numbers to headings in an array of Markdown strings and returns the combined result.

import { aktorMarkdownChapterNumbers } from '@operaide/document';

const numbered = aktorMarkdownChapterNumbers({
markdown: [markdownString],
});

aktorMarkdownChaptersTransform​

Filters Markdown content to include only the specified chapters. Chapters are identified by their number (e.g. '1', '2.3'), separated by newlines.

import { aktorMarkdownChaptersTransform } from '@operaide/document';

const selected = aktorMarkdownChaptersTransform({
markdown: numberedMarkdown,
chapters: '1\n2.3\n4',
});

URL to Markdown​

These Aktors fetch web pages and convert their HTML to Markdown using Turndown. No Document Intelligence connection is required.

aktorMarkdownFromURLs​

Fetches an array of URLs and returns their content as Markdown strings. Empty results are filtered out.

import { aktorMarkdownFromURLs } from '@operaide/document';

const markdowns = aktorMarkdownFromURLs({
urls: ['https://example.com/page1', 'https://example.com/page2'],
optionsName: 'my-options', // optional — name of registered transformation options
});

aktorCreateSearchURLs​

Builds an array of URLs from search terms and a URL template. The template must contain {{search}} as a placeholder.

import { aktorCreateSearchURLs } from '@operaide/document';

const urls = aktorCreateSearchURLs({
searchTerms: 'term one\nterm two', // newline-separated
site: 'https://example.com/search?q={{search}}',
});
// ["https://example.com/search?q=term%20one", "https://example.com/search?q=term%20two"]

Custom Transformation Hooks​

You can register custom HTML and Markdown transformation functions that are applied during URL-to-Markdown conversion:

import { registerUrlToMarkdownOptions } from '@operaide/document';
import type { TransformHtmlFn, TransformMarkdownFn } from '@operaide/document';

const transformHtml: TransformHtmlFn = (root, url) => {
root('nav').remove(); // strip navigation
root('.sidebar').remove(); // strip sidebar
return root;
};

const transformMarkdown: TransformMarkdownFn = (markdown, metadata) => {
return markdown.replace(/\[edit\]/gi, ''); // remove edit links
};

registerUrlToMarkdownOptions({
name: 'my-options',
description: 'Custom cleanup for example.com',
options: {
transformHtmlFn: transformHtml,
transformMarkdownFn: transformMarkdown,
},
});

Then reference 'my-options' as optionsName when calling aktorMarkdownFromURLs.


Common Patterns​

PDF Attachment → AI Data Extraction​

Convert an email attachment to Markdown, then extract structured data:

import { aktorMapFilesToBase64 } from '@operaide/mail';
import { createAktorFunction } from '@operaide/aktor';

// Module-level helper: aktorMapFilesToBase64 returns all PDF attachments as an
// array, so pick the first for a single-document flow.
const aktorFirstPDF = createAktorFunction('aktorFirstPDF', (args: { pdfs: string[] }) => args.pdfs[0]);

// Inside your Reaktor composition:
const base64Pdfs = aktorMapFilesToBase64({ files: attachments });
const markdown = aktorConvertPDFToMarkdown({ pdf: aktorFirstPDF({ pdfs: base64Pdfs }) });

const result = aktorAICall({
messages: aktorMessagesFromSystemAndUser({
system: aktorConst('Extract the order number, date, and total.'),
user: markdown,
}),
providerModel: aktorAISettingProviderModel(),
});

Combining with Vectorization​

Document Intelligence is the default document loader in the vectorization pipeline. When you call aktorVektorEmbeddingPipeline, the pipeline automatically uses Document Intelligence to convert uploaded PDFs to Markdown before chunking and embedding. You do not need to call aktorConvertPDFToMarkdown separately in that case.


Best Practices​

  1. Use aktorSetting for the connection name so operators can change it without editing code.

  2. Use 'default' as the connection name for standard single-connection setups.

  3. Prefer aktorConvertPDFToMarkdown over assembling lower-level conversion calls — it handles connection lookup, error wrapping, and result extraction.

  4. Lean on the vectorization pipeline when building RAG applications. It calls Document Intelligence internally, so you get conversion, chunking, and embedding in one step.

  5. Link to the admin guide in your Reaktor's documentation or settings description so operators know which connection to configure. See Document Intelligence Configuration.