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 five groups of Aktors:
| Group | Purpose |
|---|---|
| PDF Conversion | Convert PDFs (URL or base64) to Markdown |
| Word Conversion | Convert DOCX files (base64) to Markdown |
| Excel Conversion | Convert XLSX workbooks (base64) to one CSV block per sheet |
| Page & Chapter Manipulation | Split, filter, and number pages and chapters within converted Markdown |
| URL to Markdown | Fetch 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.
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
});
| Parameter | Type | Description |
|---|---|---|
pdf | string | The PDF to convert — either a URL or a data:application/pdf;base64,… string |
connectionName | string (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',
});
| Parameter | Type | Description |
|---|---|---|
pdfs | string[] | Array of PDF URLs or base64 strings |
connectionName | string (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,
});
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
});
| Parameter | Type | Description |
|---|---|---|
docx | string | DOCX 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],
});
| Parameter | Type | Description |
|---|---|---|
docxs | string[] | 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,
});
| Parameter | Type | Description |
|---|---|---|
xlsx | string | XLSX 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],
});
| Parameter | Type | Description |
|---|---|---|
xlsxs | string[] | 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:
const base64Doc = aktorEmailFilesToBase64({ files: attachments });
const markdown = aktorConvertPDFToMarkdown({ pdf: base64Doc });
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
-
Use
aktorSettingfor the connection name so operators can change it without editing code. -
Use
'default'as the connection name for standard single-connection setups. -
Prefer
aktorConvertPDFToMarkdownover assembling lower-level conversion calls — it handles connection lookup, error wrapping, and result extraction. -
Lean on the vectorization pipeline when building RAG applications. It calls Document Intelligence internally, so you get conversion, chunking, and embedding in one step.
-
Link to the admin guide in your Reaktor's documentation or settings description so operators know which connection to configure. See Document Intelligence Configuration.