document_loaders — PDF, web, S3, Notion, ... unified Document model

PDF, web, S3, Notion, ... unified Document model

Module langchain_classic/document_loaders/ — PDF, web, S3, Notion, ... unified Document model. Part of the Retrieval & Data group.

🎯 Why This Module Exists

Bridges arbitrary data sources into a uniform Document(page_content, metadata) shape. 100+ loaders cover PDFs, HTML, Notion, Confluence, GitHub, Slack, S3, Google Drive, YouTube transcripts, etc. The BaseLoader.load() returns list[Document]; lazy_load() yields one at a time for streaming ingestion of huge corpora.

166
Source files
0
Sub-directories
Retrieval & Data
Category
Python
Language

Loaders BaseLoader 100+ sources

Architecture

flowchart LR
  subgraph Core["langchain_classic/document_loaders/"]
    BASE["Base abstraction"]
    IMPLS["Concrete implementations"]
    BASE --> IMPLS
  end
  subgraph Up["Upstream callers"]
    U1["chains/"]
    U2["agents/"]
  end
  subgraph Down["Downstream"]
    D1["langchain_core"]
    D2["External providers"]
  end
  Up --> Core
  Core --> Down

Data Flow

sequenceDiagram
  participant Caller as Caller
  participant Document as document_loaders
  participant Core as langchain_core
  participant Ext as External
  Caller->>Document: invoke(input)
  Note over Document: Validate & route
  Document->>Core: delegate primitive
  Core->>Ext: provider call
  Ext-->>Core: response
  Core-->>Document: typed output
  Document-->>Caller: result

Design Decisions

DecisionChoice MadeAlternativesWhy This Approach
Document shapeDataclass: page_content (str) + metadata (dict)Per-source typeDownstream splitters/embedders need a single uniform input
Lazy iterationlazy_load() generator on top of load()Always-eagerSome sources have GBs of content; eager would OOM
Optional dependenciesPer-loader import inside the file with helpful errorHard requirements listHundreds of loaders × hundreds of clients would be unusable
Loader vs splitter separationLoaders preserve source structure; splitting is a transformerLoader splits inlineSplitter choice is application-specific (token-based, semantic, header-based)

Pairs with document_transformers/ (text splitters, redundancy filters) and feeds vectorstores/ ingestion.

Step-by-Step Walkthrough

Follow a typical operation through the document_loaders/ module:

  1. Caller invokes the public surface — usually .invoke(input) on a Runnable, or a legacy __call__(inputs_dict) on a Chain subclass.
  2. Pydantic validation — every entry point is a BaseModel; constructor args, input dicts, and tool args_schema are validated before any expensive work runs. Fail-fast on type errors.
  3. CallbackManager.configure — a fresh manager is built per call, fans on_*_start events to all subscribers (LangSmith tracer, stdout handler, custom logging), and stamps a UUID4 run_id for tree reconstruction.
  4. document_loaders-specific logic — the module's core method runs: prompt rendering, retrieval, tool execution, memory load, document split, etc. See the deep-dive sections above.
  5. Provider call — typically a single HTTP request to OpenAI/Anthropic/etc., or a local SDK call. Retries are handled by tenacity with exponential backoff and a configurable max-attempts knob.
  6. Streaming fan-out — if the provider streams tokens, each chunk fires on_llm_new_token through the callback manager so downstream observers (UI, log, tracer) see partial output.
  7. OutputParser pass — if a parser is attached, the raw provider output is parsed into a structured Python object; OutputParserException may trigger a retry chain (RetryWithErrorOutputParser).
  8. Memory write-back — for chains/agents that have a BaseMemory attached, save_context(inputs, outputs) persists the turn before returning to the caller.
  9. on_*_end fan-out — the callback manager closes the run span; tracers serialise the Run tree and ship it to LangSmith asynchronously.
  10. Result returned — caller receives a typed object (AIMessage, dict, BaseModel, list[Document], ...) matching the module's declared output schema.

What Could Go Wrong

Failure Mode Trigger Detection Recovery
Provider 5xx / rate-limit OpenAI/Anthropic/etc. transient error HTTP status from SDK tenacity retries with exponential backoff up to max_retries; then surface to caller
Output parser failure LLM returned malformed JSON / wrong schema OutputParserException in parser RetryWithErrorOutputParser re-prompts with the parser error included; or surface
Pydantic validation Caller passes wrong-shaped dict ValidationError at __init__ / invoke Surface immediately — no fallback is correct here
Callback handler exception User-supplied tracer raises Try/except around each fan-out Logged and swallowed by default; raise_error=True to opt in to propagation
Provider auth missing OPENAI_API_KEY unset SDK AuthenticationError Surface with a helpful message; no retry (would just fail again)

Performance Profile

Hot Paths

Resource Usage

Directory Map

Source tree for document_loaders
document_loaders/
├── __init__.py
├── acreom.py
├── airbyte.py
├── airbyte_json.py
├── airtable.py
├── apify_dataset.py
├── arcgis_loader.py
├── arxiv.py
├── assemblyai.py
├── async_html.py
├── azlyrics.py
├── azure_ai_data.py
├── azure_blob_storage_container.py
├── azure_blob_storage_file.py
├── baiducloud_bos_directory.py
├── baiducloud_bos_file.py
├── base.py
├── base_o365.py
├── bibtex.py
├── bigquery.py
├── bilibili.py
├── blackboard.py
├── blob_loaders/
├── blockchain.py
├── brave_search.py
├── browserless.py
├── chatgpt.py
├── chromium.py
├── college_confidential.py
├── concurrent.py
├── confluence.py
├── conllu.py
├── couchbase.py
├── csv_loader.py
├── cube_semantic.py
├── datadog_logs.py
├── dataframe.py
├── diffbot.py
├── directory.py
├── discord.py
├── docugami.py
├── docusaurus.py
├── dropbox.py
├── duckdb_loader.py
├── email.py
├── epub.py
├── etherscan.py
├── evernote.py
├── excel.py
├── facebook_chat.py
├── fauna.py
├── figma.py
├── gcs_directory.py
├── gcs_file.py
├── generic.py
├── geodataframe.py
├── git.py
├── gitbook.py
├── github.py
├── google_speech_to_text.py
    ... (more files truncated)