> ## Documentation Index
> Fetch the complete documentation index at: https://docs.definable.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# File Readers

> Extract text and multimodal content from files (PDF, DOCX, PPTX, XLSX, images, audio) and inject into agent context automatically.

File readers extract content from files attached to agent messages before they are sent to the model. This lets agents process PDFs, Word documents, presentations, spreadsheets, images, and audio files without manual preprocessing.

<Note>
  **File readers** (`definable.reader`) extract content from files attached to agent messages for LLM processing. They are distinct from **Knowledge readers** (`definable.knowledge.reader`), which convert raw sources into `Document` objects for the [RAG pipeline](/knowledge/readers).
</Note>

## Quick Example

```python theme={null}
from definable.agent import Agent
from definable.media import File
from definable.model import OpenAIChat

agent = Agent(
  model=OpenAIChat(id="gpt-4o"),
  instructions="You are a helpful assistant. Analyze any files the user provides.",
  readers=True,  # Auto-creates a registry with all available parsers
)

file = File(
  content=b"Q3 Revenue: $2.5M\nQ3 Expenses: $1.8M\nQ3 Net Income: $700K",
  filename="financials.txt",
  mime_type="text/plain",
)

output = agent.run("Summarize the key financial metrics.", files=[file])
print(output.content)
```

## Architecture

The readers module uses a layered design:

* **Parsers** — stateless format converters: `bytes → List[ContentBlock]`. Never do I/O.
* **ParserRegistry** — priority-based mapping from format to parser.
* **BaseReader** — orchestrator: `File → bytes → detect format → parse → ReaderOutput`.
* **Providers** — AI-backed readers (e.g., MistralReader) that handle their own API I/O.

## Built-in Parsers

| Parser        | Formats                                                         | Dependency           |
| ------------- | --------------------------------------------------------------- | -------------------- |
| `TextParser`  | `.txt`, `.md`, `.csv`, `.json`, `.py`, `.js`, +40 more          | None                 |
| `PDFParser`   | `.pdf`                                                          | `pypdf>=4.0.0`       |
| `DocxParser`  | `.docx`                                                         | `python-docx>=1.0.0` |
| `PptxParser`  | `.pptx`                                                         | `python-pptx>=1.0.0` |
| `XlsxParser`  | `.xlsx`                                                         | `openpyxl>=3.1.0`    |
| `OdsParser`   | `.ods`                                                          | `odfpy>=1.4.0`       |
| `RtfParser`   | `.rtf`                                                          | `striprtf>=0.0.26`   |
| `HTMLParser`  | `.html`, `.htm`                                                 | None                 |
| `ImageParser` | `.png`, `.jpg`, `.gif`, `.bmp`, `.tiff`, `.webp`, `.svg`, +more | None                 |
| `AudioParser` | `.mp3`, `.wav`, `.ogg`, `.flac`, `.m4a`, `.webm`                | None                 |

Install all parser dependencies at once:

```bash theme={null}
pip install 'definable[readers]'
```

<Tip>
  Parsers with missing optional dependencies are silently skipped. Install only what you need.
</Tip>

## ContentBlock

Content extraction produces `ContentBlock` objects — the multimodal output unit:

| Field          | Type           | Description                                           |
| -------------- | -------------- | ----------------------------------------------------- |
| `content_type` | `str`          | `"text"`, `"image"`, `"table"`, `"audio"`, or `"raw"` |
| `content`      | `str \| bytes` | Extracted content                                     |
| `mime_type`    | `str \| None`  | MIME type of the content                              |
| `metadata`     | `dict`         | Parser-specific metadata                              |
| `page_number`  | `int \| None`  | Page number (for paginated formats)                   |

**Methods:**

| Method                 | Description                                         |
| ---------------------- | --------------------------------------------------- |
| `as_text()`            | String representation of the content                |
| `as_message_content()` | OpenAI-format content part for message construction |

## ReaderOutput

Every file read returns a `ReaderOutput`:

| Field        | Type                 | Description                       |
| ------------ | -------------------- | --------------------------------- |
| `filename`   | `str`                | Name of the file                  |
| `blocks`     | `List[ContentBlock]` | Extracted content blocks          |
| `mime_type`  | `str \| None`        | Detected MIME type                |
| `page_count` | `int \| None`        | Number of pages (PDF, DOCX, PPTX) |
| `word_count` | `int \| None`        | Word count of extracted text      |
| `truncated`  | `bool`               | Whether content was truncated     |
| `error`      | `str \| None`        | Error message if reading failed   |
| `metadata`   | `dict`               | Additional metadata               |

**Methods:**

| Method                      | Description                                           |
| --------------------------- | ----------------------------------------------------- |
| `as_text(separator="\n\n")` | Concatenated text from all blocks                     |
| `as_messages()`             | OpenAI-format message content list                    |
| `content`                   | Property — backwards-compatible alias for `as_text()` |

## BaseReader

The main orchestrator that resolves files to parsed content:

```python theme={null}
from definable.reader import BaseReader

reader = BaseReader()
result = reader.read(file)
print(result.content)
```

### Constructor

<ParamField path="config" type="ReaderConfig">
  Reader configuration (file size limits, encoding, timeout).
</ParamField>

<ParamField path="registry" type="ParserRegistry">
  Custom parser registry. When `None`, a default registry with all available parsers is created.
</ParamField>

### Methods

| Method                           | Description                                   |
| -------------------------------- | --------------------------------------------- |
| `register(parser, priority=100)` | Register a parser (returns self for chaining) |
| `get_parser(file)`               | Get the parser that handles a file, or `None` |
| `read(file)`                     | Read a file synchronously                     |
| `aread(file)`                    | Read a file asynchronously                    |
| `aread_all(files)`               | Read multiple files concurrently              |

## Agent Integration

Three ways to enable file readers on an agent:

```python theme={null}
# 1. Auto-registry with all available parsers
agent = Agent(model=model, readers=True)

# 2. Custom reader instance
from definable.reader import BaseReader
reader = BaseReader(config=ReaderConfig(max_file_size=10_000_000))
agent = Agent(model=model, readers=reader)

# 3. Single parser (auto-wrapped in BaseReader)
from definable.reader.parsers.base_parser import BaseParser
agent = Agent(model=model, readers=SomeParser())
```

When the agent receives files via `run(..., files=[...])`, it automatically extracts content from each file and injects it into the prompt before calling the model.

## ReaderConfig

Configure reader behavior:

```python theme={null}
from definable.reader import ReaderConfig

config = ReaderConfig(
  max_file_size=None,          # Max file size in bytes (None = unlimited)
  max_content_length=None,     # Max extracted content length (None = unlimited)
  encoding="utf-8",            # Text encoding
  timeout=30.0,                # Read timeout in seconds
)
```

## Creating a Custom Parser

Subclass `BaseParser` and implement three methods:

```python theme={null}
from typing import List, Set
from definable.reader.parsers.base_parser import BaseParser
from definable.reader.models import ContentBlock, ReaderConfig

class MarkdownParser(BaseParser):
  def supported_mime_types(self) -> List[str]:
    return ["text/markdown"]

  def supported_extensions(self) -> Set[str]:
    return {".md"}

  def parse(
    self,
    data: bytes,
    *,
    mime_type: str | None = None,
    config: ReaderConfig | None = None,
  ) -> List[ContentBlock]:
    text = data.decode(config.encoding if config else "utf-8")
    return [ContentBlock(content_type="text", content=text)]
```

Register it with a reader:

```python theme={null}
from definable.reader import BaseReader
from definable.reader.registry import ParserRegistry

registry = ParserRegistry()
registry.register(MarkdownParser(), priority=200)  # Higher priority wins
reader = BaseReader(registry=registry)
```

## MistralReader

AI-backed OCR provider using the Mistral OCR API. Handles PDFs, DOCX, PPTX, and images with high-quality extraction.

```python theme={null}
from definable.reader.providers.mistral import MistralReader

reader = MistralReader(api_key="your-key")
agent = Agent(model=model, readers=reader)
```

<ParamField path="api_key" type="str">
  Mistral API key. Falls back to `MISTRAL_API_KEY` env var.
</ParamField>

<ParamField path="model" type="str" default="mistral-ocr-latest">
  OCR model to use.
</ParamField>

<ParamField path="include_image_base64" type="bool" default="false">
  Include base64-encoded images in output blocks.
</ParamField>

<ParamField path="local_fallback" type="bool" default="true">
  Fall back to local parsers for formats Mistral doesn't support.
</ParamField>

Native formats: `.pdf`, `.docx`, `.pptx`, `.png`, `.jpg`, `.jpeg`, `.avif`

<Note>
  Requires `mistralai`: `pip install 'definable[mistral-ocr]'`
</Note>

## Parser Options

### PDFParser

```python theme={null}
from definable.reader.parsers.pdf import PDFParser
parser = PDFParser(page_separator="\n\n")
```

### DocxParser

```python theme={null}
from definable.reader.parsers.docx import DocxParser
parser = DocxParser(include_tables=True)
```

### XlsxParser

```python theme={null}
from definable.reader.parsers.xlsx import XlsxParser
parser = XlsxParser(max_rows=1000)
```

### OdsParser

```python theme={null}
from definable.reader.parsers.ods import OdsParser
parser = OdsParser(max_rows=1000)
```

## ParserRegistry

The registry maps formats to parsers with priority-based dispatch:

```python theme={null}
from definable.reader.registry import ParserRegistry

registry = ParserRegistry(include_defaults=True)  # Registers all available parsers
registry.register(MyParser(), priority=200)        # Higher priority wins

parser = registry.get_parser(mime_type="application/pdf")
```

Built-in parsers are registered at priority `0`. User-registered parsers default to priority `100`. Higher priority wins when multiple parsers handle the same format.

## ReadersConfig

Configure the readers integration on the agent via `AgentConfig`:

```python theme={null}
from definable.agent import Agent, AgentConfig, ReadersConfig

agent = Agent(
  model=model,
  config=AgentConfig(
    readers=ReadersConfig(
      enabled=True,
      registry=None,                     # Auto-create if None
      max_total_content_length=None,     # Limit total injected content
      context_format="xml",              # "xml" or "markdown"
    ),
  ),
)
```

## Standalone Usage

Use `BaseReader` without an agent for file processing pipelines:

```python theme={null}
from definable.media import File
from definable.reader import BaseReader

reader = BaseReader()

files = [
  File(content=b"Hello, world!", filename="greeting.txt", mime_type="text/plain"),
  File(content=b'{"name": "Alice"}', filename="user.json", mime_type="application/json"),
]

for file in files:
  result = reader.read(file)
  print(f"{result.filename}: {result.content[:100]}")
```

## Audio Format Normalization

The `reader.audio` module provides utilities for normalizing audio formats. This is used internally by the `audio_to_message()` function in `utils/openai.py` and can be used standalone:

```python theme={null}
from definable.reader.audio import normalize_audio_format, OPENAI_INPUT_AUDIO_FORMATS

# Converts oga/ogg/opus → wav via ffmpeg (passthrough if already wav/mp3)
out_bytes, out_fmt = normalize_audio_format(raw_bytes, "oga")
```

### Supported Aliases

| Input Extension | Resolves To            |
| --------------- | ---------------------- |
| `oga`           | `ogg` (Telegram voice) |
| `opus`          | `ogg` (raw Opus)       |
| `mpga`          | `mp3`                  |
| `mpeg`          | `mp3`                  |

If the resolved format is not in the target set (`wav`, `mp3` by default), ffmpeg is used to transcode. When ffmpeg is not installed, a `RuntimeError` is raised with install instructions.

<Note>
  For agent-level voice transcription (the common use case), use `audio_transcriber=True` on the Agent instead of calling `normalize_audio_format` directly. See [Voice Note Transcription](/models/vision-and-audio#voice-note-transcription).
</Note>

## Stream Events

When using streaming, file reads emit events:

| Event               | Key Fields                                                | Description           |
| ------------------- | --------------------------------------------------------- | --------------------- |
| `FileReadStarted`   | `file_count`                                              | File reading began    |
| `FileReadCompleted` | `file_count`, `files_read`, `files_failed`, `duration_ms` | File reading finished |
