langextract Documentation

A Python library for extracting structured information from unstructured text using LLMs with precise source grounding and interactive visualization.

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy langextract on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name langextract -p 11434:11434 --restart always ollama/ollama:0.5.4
Configured Services & Ports
โš™๏ธ ollama
image: ollama/ollama:0.5.4
๐Ÿ”Œ Ports: 127.0.0.1:11434:11434
โš™๏ธ langextract
image: build locally
docker-compose.yml
yaml (docker-compose.yml)
# Copyright 2025 Google LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

services:
  ollama:
    image: ollama/ollama:0.5.4
    ports:
      - "127.0.0.1:11434:11434"  # Bind only to localhost for security
    volumes:
      - ollama-data:/root/.ollama  # Cross-platform support
    command: serve
    healthcheck:
      test: ["CMD", "ollama", "list"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 10s

  langextract:
    build: .
    depends_on:
      ollama:
        condition: service_healthy
    environment:
      - OLLAMA_HOST=http://ollama:11434
    volumes:
      - .:/app
    command: python demo_ollama.py

volumes:
  ollama-data:

Technical Documentation: google/langextract

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

LangExtract



Table of Contents

Introduction

LangExtract is a Python library that uses LLMs to extract structured information from unstructured text documents based on user-defined instructions. It processes materials such as clinical notes or reports, identifying and organizing key details while ensuring the extracted data corresponds to the source text.

Why LangExtract?

  1. Precise Source Grounding: Maps every extraction to its exact location in the source text, enabling visual highlighting for easy traceability and verification.
  2. Reliable Structured Outputs: Enforces a consistent output schema based on your few-shot examples, leveraging controlled generation in supported models like Gemini to guarantee robust, structured results.
  3. Optimized for Long Documents: Overcomes the "needle-in-a-haystack" challenge of large document extraction by using an optimized strategy of text chunking, parallel processing, and multiple passes for higher recall.
  4. Interactive Visualization: Instantly generates a self-contained, interactive HTML file to visualize and review thousands of extracted entities in their original context.
  5. Flexible LLM Support: Supports your preferred models, from cloud-based LLMs like the Google Gemini family to local open-source models via the built-in Ollama interface.
  6. Adaptable to Any Domain: Define extraction tasks for any domain using just a few examples. LangExtract adapts to your needs without requiring any model fine-tuning.
  7. Leverages LLM World Knowledge: Utilize precise prompt wording and few-shot examples to influence how the extraction task may utilize LLM knowledge. The accuracy of any inferred information and its adherence to the task specification are contingent upon the selected LLM, the complexity of the task, the clarity of the prompt instructions, and the nature of the prompt examples.

Quick Start

Note: Using cloud-hosted models like Gemini requires an API key. See the API Key Setup section for instructions on how to get and configure your key.

Extract structured information with just a few lines of code.

1. Define Your Extraction Task

First, create a prompt that clearly describes what you want to extract. Then, provide a high-quality example to guide the model.

python
import langextract as lx
import textwrap

# 1. Define the prompt and extraction rules
prompt = textwrap.dedent("""\
    Extract characters, emotions, and relationships in order of appearance.
    Use exact text for extractions. Do not paraphrase or overlap entities.
    Provide meaningful attributes for each entity to add context.""")

# 2. Provide a high-quality example to guide the model
examples = [
    lx.data.ExampleData(
        text="ROMEO. But soft! What light through yonder window breaks? It is the east, and Juliet is the sun.",
        extractions=[
            lx.data.Extraction(
                extraction_class="character",
                extraction_text="ROMEO",
                attributes={"emotional_state": "wonder"}
            ),
            lx.data.Extraction(
                extraction_class="emotion",
                extraction_text="But soft!",
                attributes={"feeling": "gentle awe"}
            ),
            lx.data.Extraction(
                extraction_class="relationship",
                extraction_text="Juliet is the sun",
                attributes={"type": "metaphor"}
            ),
        ]
    )
]

Note: Examples drive model behavior. Each extraction_text should ideally be verbatim from the example's text (no paraphrasing), listed in order of appearance. LangExtract raises Prompt alignment warnings by default if examples don't follow this patternโ€”resolve these for best results.

Grounding: LLMs may occasionally extract content from few-shot examples rather than the input text. LangExtract automatically detects this: extractions that cannot be located in the source text will have char_interval = None. Filter these out with [e for e in result.extractions if e.char_interval] to keep only grounded results.

2. Run the Extraction

Provide your input text and the prompt materials to the lx.extract function.

python
# The input text to be processed
input_text = "Lady Juliet gazed longingly at the stars, her heart aching for Romeo"

# Run the extraction
result = lx.extract(
    text_or_documents=input_text,
    prompt_description=prompt,
    examples=examples,
    model_id="gemini-3.5-flash",
)

For advanced constraints beyond examples, such as enum values on extraction
attributes, Gemini and OpenAI support output_schema with or without
few-shot examples. See
Custom output schemas.

Model Selection: gemini-3.5-flash is the recommended default, offering strong extraction quality for LangExtract's schema-constrained workflows. For high-volume or cost-sensitive workloads, consider the current stable Flash-Lite model, gemini-3.1-flash-lite; for highly complex tasks requiring deeper reasoning, evaluate a current Gemini Pro model from the official model documentation. For large-scale or production use, a paid Gemini tier is suggested to increase throughput and avoid rate limits. See the rate-limit documentation for details.

Model Lifecycle: Note that Gemini models have a lifecycle with defined retirement dates. Users should consult the official model version documentation to stay informed about the latest stable and legacy versions.

3. Visualize the Results

The extractions can be saved to a .jsonl file, a popular format for working with language model data. LangExtract can then generate an interactive HTML visualization from this file to review the entities in context.

python
# Save the results to a JSONL file
lx.io.save_annotated_documents([result], output_name="extraction_results.jsonl", output_dir=".")

# Generate the visualization from the file
html_content = lx.visualize("extraction_results.jsonl")
with open("visualization.html", "w", encoding="utf-8") as f:
    if hasattr(html_content, 'data'):
        f.write(html_content.data)  # For Jupyter/Colab
    else:
        f.write(html_content)

This creates an animated and interactive HTML file:

Note on LLM Knowledge Utilization: This example demonstrates extractions that stay close to the text evidence - extracting "longing" for Lady Juliet's emotional state and identifying "yearning" from "gazed longingly at the stars." The task could be modified to generate attributes that draw more heavily from the LLM's world knowledge (e.g., adding "identity": "Capulet family daughter" or "literary_context": "tragic heroine"). The balance between text-evidence and knowledge-inference is controlled by your prompt instructions and example attributes.

Scaling to Longer Documents

For larger texts, you can process entire documents directly from URLs with parallel processing and enhanced sensitivity:

python
# Process Romeo & Juliet directly from Project Gutenberg
result = lx.extract(
    text_or_documents="https://www.gutenberg.org/files/1513/1513-0.txt",
    fetch_urls=True,
    prompt_description=prompt,
    examples=examples,
    model_id="gemini-3.5-flash",
    extraction_passes=3,    # Improves recall through multiple passes
    max_workers=20,         # Parallel processing for speed
    max_char_buffer=1000    # Smaller contexts for better accuracy
)

This approach can extract hundreds of entities from full novels while maintaining high accuracy. The interactive visualization seamlessly handles large result sets, making it easy to explore hundreds of entities from the output JSONL file. See the full Romeo and Juliet extraction example โ†’ for detailed results and performance insights.

Vertex AI Batch Processing

Save costs on large-scale tasks by enabling Vertex AI Batch API with
language_model_params that include vertexai=True, project, location,
and a batch config.

See an example of the Vertex AI Batch API usage in this example.

Installation

From PyPI

bash
pip install langextract

Recommended for most users. For isolated environments, consider using a virtual environment:

bash
python -m venv langextract_env
source langextract_env/bin/activate  # On Windows: langextract_env\Scripts\activate
pip install langextract

From Source

LangExtract uses modern Python packaging with pyproject.toml for dependency management:

Installing with -e puts the package in development mode, allowing you to modify the code without reinstalling.

bash
git clone https://github.com/google/langextract.git
cd langextract

# For basic installation:
pip install -e .

# For development (includes linting tools):
pip install -e ".[dev]"

# For testing (includes pytest):
pip install -e ".[test]"

Docker

bash
docker build -t langextract .
docker run --rm -e LANGEXTRACT_API_KEY="your-api-key" langextract python your_script.py

API Key Setup for Cloud Models

When using LangExtract with cloud-hosted models (like Gemini or OpenAI), you'll need to
set up an API key. On-device models don't require an API key. For developers
using local LLMs, LangExtract offers built-in support for Ollama and can be
extended to other third-party APIs by updating the inference endpoints.

API Key Sources

Get API keys from:

Setting up API key in your environment

Option 1: Environment Variable

bash
export LANGEXTRACT_API_KEY="your-api-key-here"

Option 2: .env File (Recommended)

Add your API key to a .env file:

bash
# Add API key to .env file
cat >> .env << 'EOF'
LANGEXTRACT_API_KEY=your-api-key-here
EOF

# Keep your API key secure
echo '.env' >> .gitignore

In your Python code:

python
import langextract as lx

result = lx.extract(
    text_or_documents=input_text,
    prompt_description="Extract information...",
    examples=[...],
    model_id="gemini-3.5-flash"
)

Option 3: Direct API Key (Not Recommended for Production)

You can also provide the API key directly in your code, though this is not recommended for production use:

python
result = lx.extract(
    text_or_documents=input_text,
    prompt_description="Extract information...",
    examples=[...],
    model_id="gemini-3.5-flash",
    api_key="your-api-key-here"  # Only use this for testing/development
)

Option 4: Vertex AI (Service Accounts)

Use Vertex AI for authentication with service accounts:

python
result = lx.extract(
    text_or_documents=input_text,
    prompt_description="Extract information...",
    examples=[...],
    model_id="gemini-3.5-flash",
    language_model_params={
        "vertexai": True,
        "project": "your-project-id",
        "location": "global"  # or regional endpoint
    }
)

Adding Custom Model Providers

LangExtract supports custom LLM providers via a lightweight plugin system. You can add support for new models without changing core code.

  • Add new model support independently of the core library
  • Distribute your provider as a separate Python package
  • Keep custom dependencies isolated
  • Override or extend built-in providers via priority-based resolution

See the detailed guide in Provider System Documentation to learn how to:

  • Register a provider with @router.register(...) from langextract.providers
  • Publish an entry point for discovery
  • Optionally provide a schema with get_schema_class() for structured output
  • Integrate with the factory via create_model(...)

Using OpenAI Models

LangExtract supports OpenAI models (requires optional dependency: pip install langextract[openai]):

python
import langextract as lx

# OPENAI_API_KEY in the environment is picked up automatically; pass
# api_key=... explicitly only if you need to override it.
result = lx.extract(
    text_or_documents=input_text,
    prompt_description=prompt,
    examples=examples,
    model_id="gpt-4o",  # Automatically selects OpenAI provider
)

The OpenAI provider uses structured outputs or JSON mode and auto-determines
fence behavior โ€” leave fence_output and use_schema_constraints unset.
output_schema is also supported for OpenAI models that support structured
outputs; provide a LangExtract output-envelope JSON schema, preferably with the
lx.schema helpers.

For large, non-latency-sensitive OpenAI workloads, enable the OpenAI Batch API
with language_model_params. Batch mode is opt-in and falls back to realtime
calls when the prompt count is below the configured threshold.

python
result = lx.extract(
    text_or_documents=documents,
    prompt_description=prompt,
    examples=examples,
    model_id="gpt-4o-mini",
    language_model_params={
        "batch": {
            "enabled": True,
            "threshold": 50,
            "poll_interval": 10,
        }
    },
)

For OpenAI-compatible endpoints or non-GPT model IDs (which skip auto-routing), use ModelConfig with an explicit provider:

python
from langextract.factory import ModelConfig

result = lx.extract(
    text_or_documents=input_text,
    prompt_description=prompt,
    examples=examples,
    config=ModelConfig(
        model_id="my-openai-compatible-model",
        provider="openai",
        provider_kwargs={"api_key": "sk-...", "base_url": "https://..."},
    ),
)

Using Local LLMs with Ollama

LangExtract supports local inference using Ollama, allowing you to run models without API keys:

python
import langextract as lx

result = lx.extract(
    text_or_documents=input_text,
    prompt_description=prompt,
    examples=examples,
    model_id="gemma2:2b",  # Automatically selects Ollama provider
    model_url="http://localhost:11434",
)

The Ollama provider exposes FormatModeSchema for JSON mode. Leave fence_output
and use_schema_constraints unset so the factory auto-configures from the provider's
schema. Ollama does not currently support output_schema.

Quick setup: Install Ollama from ollama.com, run ollama pull gemma2:2b, then ollama serve.

For detailed installation, Docker setup, and examples, see examples/ollama/.

More Examples

Additional examples of LangExtract in action:

*Romeo and Juliet* Full Text Extraction

LangExtract can process complete documents directly from URLs. This example demonstrates extraction from the full text of Romeo and Juliet from Project Gutenberg (147,843 characters), showing parallel processing, sequential extraction passes, and performance optimization for long document processing.

View Romeo and Juliet Full Text Example โ†’

Medication Extraction

Disclaimer: This demonstration is for illustrative purposes of LangExtract's baseline capability only. It does not represent a finished or approved product, is not intended to diagnose or suggest treatment of any disease or condition, and should not be used for medical advice.

LangExtract excels at extracting structured medical information from clinical text. These examples demonstrate both basic entity recognition (medication names, dosages, routes) and relationship extraction (connecting medications to their attributes), showing LangExtract's effectiveness for healthcare applications.

View Medication Examples โ†’

Radiology Report Structuring: RadExtract

Explore RadExtract, a live interactive demo on HuggingFace Spaces that shows how LangExtract can automatically structure radiology reports. Try it directly in your browser with no setup required.

View RadExtract Demo โ†’

Community Providers

Extend LangExtract with custom model providers! Check out our Community Provider Plugins registry to discover providers created by the community or add your own.

For detailed instructions on creating a provider plugin, see the Custom Provider Plugin Example.

Contributing

Contributions are welcome! See CONTRIBUTING.md to get started
with development, testing, and pull requests. You must sign a
Contributor License Agreement
before submitting patches.

Thanks to everyone who has contributed.

Testing

To run tests locally from the source:

bash
# Clone the repository
git clone https://github.com/google/langextract.git
cd langextract

# Install with test dependencies
pip install -e ".[test]"

# Run all tests
pytest tests

Or reproduce the full CI matrix locally with tox:

bash
tox  # runs pylint + pytest on Python 3.10 and 3.11

Ollama Integration Testing

If you have Ollama installed locally, you can run integration tests:

bash
# Test Ollama integration (requires Ollama running with gemma2:2b model)
tox -e ollama-integration

This test will automatically detect if Ollama is available and run real inference tests.

Development

Code Formatting

This project uses automated formatting tools to maintain consistent code style:

bash
# Auto-format all code
./autoformat.sh

# Or run formatters separately
isort langextract tests --profile google --line-length 80
pyink langextract tests --config pyproject.toml

Pre-commit Hooks

For automatic formatting checks:

bash
pre-commit install  # One-time setup
pre-commit run --all-files  # Manual run

Linting

Run linting before submitting PRs:

bash
pylint --rcfile=.pylintrc langextract tests

See CONTRIBUTING.md for full development guidelines.

How to Cite

If you use LangExtract in your research, please cite it:

bibtex
@software{goel_langextract,
  author  = {Goel, Akshay},
  title   = {{LangExtract}},
  year    = {2026},
  version = {1.6.0},
  doi     = {10.5281/zenodo.21126643},
  url     = {https://github.com/google/langextract}
}

Cite the version you used โ€” each release has its own DOI on
Zenodo. If your style rejects
@software, use @misc.

Disclaimer

This is not an officially supported Google product. If you use
LangExtract in production or publications, please cite accordingly and
acknowledge usage. Use is subject to the Apache 2.0 License.
For health-related applications, use of LangExtract is also subject to the
Health AI Developer Foundations Terms of Use.


Happy Extracting!


Overview (`examples/custom_provider_plugin/README.md`)

Custom Provider Plugin Example

This example demonstrates how to create a custom provider plugin that extends LangExtract with your own model backend.

Note: This is an example included in the LangExtract repository for reference. It is not part of the LangExtract package and won't be installed when you pip install langextract.

Automated Creation: Instead of manually copying this example, use the provider plugin generator script:

bash
python scripts/create_provider_plugin.py MyProvider --with-schema

This will create a complete plugin structure with all boilerplate code ready for customization.

Structure

text
custom_provider_plugin/
โ”œโ”€โ”€ pyproject.toml                      # Package configuration and metadata
โ”œโ”€โ”€ README.md                            # This file
โ”œโ”€โ”€ langextract_provider_example/        # Package directory
โ”‚   โ”œโ”€โ”€ __init__.py                     # Package initialization
โ”‚   โ”œโ”€โ”€ provider.py                     # Custom provider implementation
โ”‚   โ””โ”€โ”€ schema.py                       # Custom schema implementation (optional)
โ””โ”€โ”€ test_example_provider.py            # Test script

Key Components

Provider Implementation (`provider.py`)

python
from langextract.core import base_model
from langextract.providers import router

@router.register(
    r'^gemini',  # Pattern for model IDs this provider handles
)
class CustomGeminiProvider(base_model.BaseLanguageModel):
    def __init__(self, model_id: str, **kwargs):
        # Initialize your backend client

    def infer(self, batch_prompts, **kwargs):
        # Call your backend API and return results

Package Configuration (`pyproject.toml`)

toml
[project.entry-points."langextract.providers"]
custom_gemini = "langextract_provider_example:CustomGeminiProvider"

This entry point allows LangExtract to automatically discover your provider.

Custom Schema Support (`schema.py`)

Providers can optionally implement custom schemas for structured output:

Flow: Examples โ†’ from_examples() โ†’ to_provider_config() โ†’ Provider kwargs โ†’ Inference

python
from langextract.core import schema as core_schema

class CustomProviderSchema(core_schema.BaseSchema):
    @classmethod
    def from_examples(cls, examples_data, attribute_suffix="_attributes"):
        # Analyze examples to find patterns
        # Build schema based on extraction classes and attributes seen
        return cls(schema_dict)

    def to_provider_config(self):
        # Convert schema to provider kwargs
        return {
            "response_schema": self._schema_dict,
            "enable_structured_output": True
        }

    @property
    def requires_raw_output(self):
        # True = provider emits raw JSON, no markdown fences needed
        return True

Then in your provider:

python
class CustomProvider(base_model.BaseLanguageModel):
    @classmethod
    def get_schema_class(cls):
        return CustomProviderSchema  # Tell LangExtract about your schema

    def __init__(self, **kwargs):
        # Receive schema config in kwargs when use_schema_constraints=True
        self.response_schema = kwargs.get('response_schema')

    def infer(self, batch_prompts, **kwargs):
        # Use schema during API calls
        if self.response_schema:
            config['response_schema'] = self.response_schema

Installation

bash
# Navigate to this example directory first
cd examples/custom_provider_plugin

# Install in development mode
pip install -e .

# Test the provider (must be run from this directory)
python test_example_provider.py

Usage

Since this example registers the same pattern as the default Gemini provider, you must explicitly specify it:

python
import langextract as lx

# Option A: build a model explicitly and pass it to extract()
config = lx.factory.ModelConfig(
    model_id="gemini-3.5-flash",
    provider="CustomGeminiProvider",
    provider_kwargs={"api_key": "your-api-key"},
)
model = lx.factory.create_model(config)

result = lx.extract(
    text_or_documents="Your text here",
    model=model,
    prompt_description="Extract key information",
    examples=[...],
)

# Option B: let extract() build the model from a ModelConfig
result = lx.extract(
    text_or_documents="Your text here",
    config=lx.factory.ModelConfig(
        model_id="gemini-3.5-flash",
        provider="CustomGeminiProvider",
        provider_kwargs={"api_key": "your-api-key"},
    ),
    prompt_description="Extract key information",
    examples=[...],
)

Creating Your Own Provider - Step by Step

1. Copy and Rename

bash
# Copy this example directory
cp -r examples/custom_provider_plugin/ ~/langextract-myprovider/

# Rename the package directory
cd ~/langextract-myprovider/
mv langextract_provider_example langextract_myprovider

2. Update Package Configuration

Edit pyproject.toml:

  • Change name = "langextract-myprovider"
  • Update description and author information
  • Change entry point: myprovider = "langextract_myprovider:MyProvider"

3. Modify Provider Implementation

Edit provider.py:

  • Change class name from CustomGeminiProvider to MyProvider
  • Update @router.register(...) patterns to match your model IDs
  • Replace Gemini API calls with your backend
  • Add any provider-specific parameters

4. Add Schema Support (Optional)

Edit schema.py:

  • Rename to MyProviderSchema
  • Customize from_examples() for your extraction format
  • Update to_provider_config() for your API requirements
  • Implement requires_raw_output (abstract in BaseSchema) based on whether your provider emits raw JSON/YAML or fenced output

5. Install and Test

bash
# Install in development mode
pip install -e .

# Test your provider
python -c "
from langextract.providers import load_plugins_once, router
load_plugins_once()
print('Provider registered:', any('myprovider' in str(e) for e in router.list_entries()))
"

6. Write Tests

  • Test that your provider loads and handles basic inference
  • Verify schema support works (if implemented)
  • Test error handling for your specific API

7. Publish to PyPI and Share with Community

bash
# Build package
python -m build

# Upload to PyPI
twine upload dist/*

Share with the community:

Common Pitfalls to Avoid

  1. Forgetting to trigger plugin loading - Plugins load lazily, use load_plugins_once() in tests
  2. Pattern conflicts - Avoid patterns that conflict with built-in providers
  3. Missing dependencies - List all requirements in pyproject.toml
  4. Schema mismatches - Test schema generation with real examples
  5. Not handling None schema - Provider must clear schema when apply_schema(None) is called (see provider.py for implementation)

License

Apache License 2.0


Overview (`examples/ollama/README.md`)

Ollama Examples

This directory contains examples for using LangExtract with Ollama for local LLM inference.

For setup instructions and documentation, see the main README's Ollama section.

Quick Reference

Option 1: Run locally

bash
# Install and start Ollama
ollama pull gemma2:2b
ollama serve  # Keep this running in a separate terminal

# Run the demo
python demo_ollama.py

Option 2: Run with Docker

bash
# Runs both Ollama and the demo in containers
docker-compose up

Files

  • demo_ollama.py - Comprehensive extraction examples demonstrating Ollama on README examples
  • docker-compose.yml - Production-ready Docker setup with health checks
  • Dockerfile - Container definition for LangExtract

Configuration Options

Timeout Settings

For slower models or large prompts, you may need to increase the timeout (default: 120 seconds):

python
import langextract as lx

result = lx.extract(
    text_or_documents=input_text,
    prompt_description=prompt,
    examples=examples,
    model_id="llama3.1:70b",  # Larger model may need more time
    timeout=300,  # 5 minutes
    model_url="http://localhost:11434",
)

Or using ModelConfig:

python
config = lx.factory.ModelConfig(
    model_id="llama3.1:70b",
    provider_kwargs={
        "model_url": "http://localhost:11434",
        "timeout": 300,  # 5 minutes
    }
)

Model License

Ollama models come with their own licenses. For example:

Please review the license for any model you use.


Overview (`langextract/_compat/README.md`)

Backward Compatibility Layer

This directory contains backward compatibility shims for deprecated imports.

Deprecation Timeline

All code in this directory will be removed in LangExtract v2.0.0.

Migration Guide

The following imports are deprecated and should be updated:

Inference Module

  • from langextract.inference import BaseLanguageModel โ†’ from langextract.core.base_model import BaseLanguageModel
  • from langextract.inference import ScoredOutput โ†’ from langextract.core.types import ScoredOutput
  • from langextract.inference import InferenceOutputError โ†’ from langextract.core.exceptions import InferenceOutputError
  • from langextract.inference import GeminiLanguageModel โ†’ from langextract.providers.gemini import GeminiLanguageModel
  • from langextract.inference import OpenAILanguageModel โ†’ from langextract.providers.openai import OpenAILanguageModel
  • from langextract.inference import OllamaLanguageModel โ†’ from langextract.providers.ollama import OllamaLanguageModel

Schema Module

  • from langextract.schema import BaseSchema โ†’ from langextract.core.schema import BaseSchema
  • from langextract.schema import Constraint โ†’ from langextract.core.schema import Constraint
  • from langextract.schema import ConstraintType โ†’ from langextract.core.schema import ConstraintType
  • from langextract.schema import EXTRACTIONS_KEY โ†’ from langextract.core.schema import EXTRACTIONS_KEY
  • from langextract.schema import GeminiSchema โ†’ from langextract.providers.schemas.gemini import GeminiSchema

Exceptions Module

  • All exceptions: from langextract.exceptions import * โ†’ from langextract.core.exceptions import *

Registry Module

  • from langextract.registry import * โ†’ from langextract.plugins import *
  • from langextract.providers.registry import * โ†’ from langextract.providers.router import *

For Contributors

Do not add new code to this directory. All new development should use the canonical imports from core/ and providers/.


Overview (`langextract/providers/README.md`)

LangExtract Provider System

This directory contains the provider system for LangExtract, which enables support for different Large Language Model (LLM) backends.

Quick Start: Use the provider plugin generator script to create a new provider in minutes:

bash
python scripts/create_provider_plugin.py MyProvider --with-schema

Architecture Overview

The provider system uses a router pattern with automatic discovery:

  1. Router (router.py): Maps model ID patterns to provider classes. A legacy registry.py alias still imports โ€” new code should use router directly.
  2. Factory (../factory.py): Creates provider instances based on model IDs
  3. Providers: Implement the BaseLanguageModel interface (from langextract.core.base_model)

Provider Resolution Flow

text
User Code                    LangExtract                      Provider
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                    โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                      โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    |                             |                              |
    | lx.extract(                 |                              |
    |   model_id="gemini-3.5-flash")                             |
    |โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>                              |
    |                             |                              |
    |                    factory.create_model()                  |
    |                             |                              |
    |                    router.resolve("gemini-3.5-flash")      |
    |                       Pattern match: ^gemini               |
    |                             โ†“                              |
    |                       GeminiLanguageModel                  |
    |                             |                              |
    |                    Instantiate provider                    |
    |                             |โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>|
    |                             |                              |
    |                             |       Provider API calls     |
    |                             |<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€|
    |                             |                              |
    |<โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                               |
    | AnnotatedDocument           |                              |

Explicit Provider Selection

When multiple providers might support the same model ID, or when you want to use a specific provider, you can explicitly specify the provider:

python
import langextract as lx

# Method 1: Using factory directly with provider parameter
config = lx.factory.ModelConfig(
    model_id="gpt-4",
    provider="OpenAILanguageModel",  # Explicit provider
    provider_kwargs={"api_key": "..."}
)
model = lx.factory.create_model(config)

# Method 2: Using provider without model_id (uses provider's default)
config = lx.factory.ModelConfig(
    provider="GeminiLanguageModel",  # Will use default gemini-3.5-flash
    provider_kwargs={"api_key": "..."}
)
model = lx.factory.create_model(config)

# Method 3: Auto-detection (when no conflicts exist)
config = lx.factory.ModelConfig(
    model_id="gemini-3.5-flash"  # Provider auto-detected
)
model = lx.factory.create_model(config)

Provider names can be:

  • Full class name: "GeminiLanguageModel", "OpenAILanguageModel", "OllamaLanguageModel"
  • Partial match: "gemini", "openai", "ollama" (case-insensitive)

Provider Types

1. Core Providers (Always Available)

Ships with langextract, dependencies included:

  • Gemini (gemini.py): Google's Gemini models
  • Ollama (ollama.py): Local models via Ollama

2. Built-in Provider with Optional Dependencies

Ships with langextract, but requires extra installation:

  • OpenAI (openai.py): OpenAI's GPT models
    • Code included in package
    • Requires: pip install langextract[openai] to install OpenAI SDK
    • Future: May be moved to external plugin package

3. External Plugins (Third-party)

Separate packages that extend LangExtract with new providers:

  • Installed separately: pip install langextract-yourprovider
  • Auto-discovered: Uses Python entry points for automatic registration
  • Zero configuration: Import langextract and the provider is available
  • Independent updates: Update providers without touching core
python
# Install a third-party provider
pip install langextract-yourprovider

# Use it immediately - no imports needed!
import langextract as lx
result = lx.extract(
    text_or_documents="...",
    prompt_description="Extract key information",
    examples=[...],
    model_id="yourmodel-latest",  # Automatically finds the provider
)

How Plugin Discovery Works

text
1. pip install langextract-yourprovider
   โ””โ”€โ”€ Installs package containing:
       โ€ข Provider class with @router.register decorator
       โ€ข Python entry point pointing to this class

2. import langextract
   โ””โ”€โ”€ Loads providers/__init__.py
       โ””โ”€โ”€ Plugin loading is lazy (on-demand)

3. lx.extract(model_id="yourmodel-latest")
   โ””โ”€โ”€ Triggers plugin discovery via entry points
       โ””โ”€โ”€ @router.register decorator fires
           โ””โ”€โ”€ Provider patterns added to router
               โ””โ”€โ”€ Router matches pattern and uses your provider

Important Notes:

  • Plugin loading is lazy - plugins are discovered when first needed
  • To manually trigger plugin loading: lx.providers.load_plugins_once()
  • Set LANGEXTRACT_DISABLE_PLUGINS=1 to disable plugin loading
  • Registry entries are tuples: (patterns_list, priority_int)

How Provider Selection Works

When you call lx.extract(model_id="gemini-3.5-flash", ...), here's what happens:

  1. Factory receives model_id: "gemini-3.5-flash"
  2. Router searches patterns: Each provider registers regex patterns
  3. First match wins: Returns the matching provider class
  4. Provider instantiated: With model_id and any kwargs
  5. Inference runs: Using the selected provider

Pattern Registration Example

python
from langextract.core import base_model
from langextract.providers import router

# Gemini provider registration:
@router.register(
    r'^GeminiLanguageModel$',  # Explicit: model_id="GeminiLanguageModel"
    r'^gemini',                # Prefix: model_id="gemini-3.5-flash"
    r'^palm'                   # Legacy: model_id="palm-2"
)
class GeminiLanguageModel(base_model.BaseLanguageModel):
    def __init__(self, model_id: str, api_key: str = None, **kwargs):
        # Initialize Gemini client
        ...

    def infer(self, batch_prompts, **kwargs):
        # Call Gemini API
        ...

Usage Examples

Using Default Provider Selection

python
import langextract as lx

# Automatically selects Gemini provider
result = lx.extract(
    text_or_documents="...",
    prompt_description="Extract key information",
    examples=[...],
    model_id="gemini-3.5-flash",
)

Passing Parameters to Providers

Parameters flow from lx.extract() to providers through several mechanisms:

python
# 1. Common parameters handled by lx.extract itself:
result = lx.extract(
    text_or_documents="Your document",
    model_id="gemini-3.5-flash",
    prompt_description="Extract key facts",
    examples=[...],             # Required unless output_schema is provided
    max_workers=4,              # Parallel processing (provider-dependent)
    max_char_buffer=3000,       # Document chunking
)

# 2. Provider-specific constructor parameters via language_model_params:
result = lx.extract(
    text_or_documents="Your document",
    model_id="gemini-3.5-flash",
    prompt_description="Extract entities",
    examples=[...],
    temperature=0.7,     # Common sampling parameter
    api_key="your-key",  # Override environment variable
    language_model_params={
        "max_output_tokens": 1000,
        "top_p": 0.95,
        "top_k": 40,
    },
)

Using the Factory for Advanced Control

python
# When you need explicit provider selection or advanced configuration
from langextract import factory

# Specify both model and provider (useful when multiple providers support same model)
config = factory.ModelConfig(
    model_id="gemma2:2b",
    provider="OllamaLanguageModel",  # Explicitly use Ollama
    provider_kwargs={
        "model_url": "http://localhost:11434"
    }
)
model = factory.create_model(config)

Direct Provider Usage

python
import langextract as lx

# Direct import if you prefer (optional)
from langextract.providers.gemini import GeminiLanguageModel

model = GeminiLanguageModel(
    model_id="gemini-3.5-flash",
    api_key="your-key"
)
outputs = model.infer(["prompt1", "prompt2"])

Creating a New Provider

๐Ÿ“ Complete Example: See examples/custom_provider_plugin/ for a fully-functional plugin template with testing and documentation.

Quick Start Checklist

Creating a provider plugin? Follow this checklist:

โ˜ **1. Setup Package Structure**

text
langextract-yourprovider/
โ”œโ”€โ”€ pyproject.toml              # Package config with entry point
โ”œโ”€โ”€ README.md                    # Documentation
โ”œโ”€โ”€ LICENSE                      # License file
โ””โ”€โ”€ langextract_yourprovider/   # Package directory
    โ”œโ”€โ”€ __init__.py             # Exports provider class
    โ”œโ”€โ”€ provider.py             # Provider implementation
    โ””โ”€โ”€ schema.py               # (Optional) Custom schema

โ˜ **2. Configure Entry Point** (`pyproject.toml`)

toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "langextract-yourprovider"
version = "0.1.0"
dependencies = ["langextract>=1.0.0"]

[project.entry-points."langextract.providers"]
yourprovider = "langextract_yourprovider:YourProviderLanguageModel"

โ˜ **3. Implement Provider** (`provider.py`)

  • Import required modules
  • Add @router.register() decorator with patterns
  • Inherit from base_model.BaseLanguageModel
  • Implement __init__() method
  • Implement infer() method returning ScoredOutput objects
  • Export class from __init__.py

โ˜ **4. (Optional) Add Schema Support** (`schema.py`)

  • Create schema class inheriting from langextract.core.schema.BaseSchema
  • Implement from_examples() class method
  • Implement from_schema_dict() if the provider supports output_schema
  • Implement to_provider_config() method
  • Add get_schema_class() to provider
  • Handle schema in provider's __init__() and infer()

โ˜ **5. Testing**

  • Install plugin with pip install -e .
  • Test that your provider loads and handles basic inference
  • Verify schema support works (if implemented)

โ˜ **6. Documentation**

  • Document supported model IDs and patterns
  • List required environment variables
  • Provide usage examples
  • Document any provider-specific parameters

โ˜ **7. Distribution & Community**

  • Test installation with pip install -e .
  • Build package with python -m build
  • Test in clean environment
  • Publish to PyPI with twine upload dist/*
  • Share your provider by opening an issue on LangExtract GitHub to get feedback and help others discover it
  • Consider submitting a PR to add your provider to COMMUNITY_PROVIDERS.md

External plugins are the recommended approach for adding new providers. They're easy to maintain, distribute, and don't require changes to the core package.

For Users (Installing an External Plugin)

Simply install the plugin package:

bash
pip install langextract-yourprovider
# That's it! The provider is now available in langextract

For Developers (Creating an External Plugin)

  1. Create a new package:
text
langextract-myprovider/
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ langextract_myprovider/
    โ””โ”€โ”€ __init__.py
  1. Configure entry point in pyproject.toml:
toml
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "langextract-myprovider"
version = "0.1.0"
dependencies = ["langextract>=1.0.0", "your-sdk"]

[project.entry-points."langextract.providers"]
# Pattern 1: Register the class directly
myprovider = "langextract_myprovider:MyProviderLanguageModel"

# Pattern 2: Register a module that self-registers
# myprovider = "langextract_myprovider"
  1. Implement your provider:
python
# langextract_myprovider/__init__.py
import os

from langextract.core import base_model, types
from langextract.providers import router

@router.register(r'^mymodel', r'^custom', priority=10)
class MyProviderLanguageModel(base_model.BaseLanguageModel):
    def __init__(self, model_id: str, api_key: str = None, **kwargs):
        super().__init__()
        self.model_id = model_id
        self.api_key = api_key or os.environ.get('MYPROVIDER_API_KEY')
        # Initialize your client
        self.client = MyProviderClient(api_key=self.api_key)

    def infer(self, batch_prompts, **kwargs):
        # Implement inference
        for prompt in batch_prompts:
            result = self.client.generate(prompt, **kwargs)
            yield [types.ScoredOutput(score=1.0, output=result)]

The built-in providers keep SDK response types inside the provider. A local
helper such as _response_to_scored_output() translates usable text into
ScoredOutput and raises a provider-tagged InferenceRuntimeError for blocked,
refused, truncated, or otherwise unusable responses. infer() remains the
public plugin contract; the helper is a convention, not a required base method.
Providers using this pattern should re-raise InferenceRuntimeError before any
retry or blanket exception handler so the diagnostic is preserved.

Pattern Registration Explained:

  • The @router.register decorator patterns (e.g., r'^mymodel', r'^custom') define which model IDs your provider supports
  • When users call lx.extract(model_id="mymodel-3b"), the router matches against these patterns
  • Your provider will handle any model_id starting with "mymodel" or "custom"
  • Users can explicitly select your provider using its class name:
    python
    config = lx.factory.ModelConfig(provider="MyProviderLanguageModel")
    # Or partial match: provider="myprovider" (matches class name)
  1. Publish your package to PyPI:
bash
pip install build twine
python -m build
twine upload dist/*

Now users can install and use your provider with just pip install langextract-myprovider!

Adding Schema Support

Schemas enable structured output with strict JSON constraints. Here's how to add schema support to your provider:

1. Create a Schema Class

text
/* Detailed source-code truncated for AI context efficiency. */

2. Update Your Provider

text
/* Detailed source-code truncated for AI context efficiency. */

3. Schema Usage

When users set use_schema_constraints=True, LangExtract will:

  1. Call your provider's get_schema_class()
  2. Use from_examples() to build a schema from provided examples
  3. Call to_provider_config() to get provider-specific kwargs
  4. Pass these kwargs to your provider's __init__()
  5. Your provider uses the schema for structured output

For user-authored schemas passed as output_schema=..., also implement
BaseSchema.from_schema_dict(). If the provider exposes lower-level schema
kwargs such as response_schema, list them in
output_schema_reserved_provider_kwargs() so LangExtract can reject ambiguous
requests where both output_schema and provider-native schema kwargs are set.
Schemas returned from from_schema_dict() should expose
from_output_schema=True; BaseLanguageModel.apply_output_schema() marks
mutable schema instances automatically, but explicit state is safest for
dataclass or slots-based schemas.
Providers that store schema state after construction should override
apply_schema() and call super().apply_schema(schema_instance).

Option 2: Built-in Provider (Requires Core Team Approval)

โš ๏ธ Note: Adding a provider to the core package requires:

  • Significant community demand and support
  • Commitment to long-term maintenance
  • Approval from the LangExtract maintainers
  • A pull request to the main repository

This approach should only be used for providers that benefit a large portion of the user base.

  1. Create your provider file:
python
# langextract/providers/myprovider.py
from langextract.core import base_model
from langextract.providers import router

@router.register(r'^mymodel', r'^custom')
class MyProviderLanguageModel(base_model.BaseLanguageModel):
    # Implementation same as above
  1. Import it in providers/__init__.py:
python
# In langextract/providers/__init__.py
from langextract.providers import myprovider  # noqa: F401
  1. Submit a pull request with:
    • Provider implementation
    • Comprehensive tests
    • Documentation
    • Justification for inclusion in core

Environment Variables

The factory automatically resolves API keys from environment:

Provider Environment Variables (in priority order)
Gemini GEMINI_API_KEY, LANGEXTRACT_API_KEY
OpenAI OPENAI_API_KEY, LANGEXTRACT_API_KEY
Ollama OLLAMA_BASE_URL (default: http://localhost:11434)

Design Principles

  1. Zero Configuration: Providers auto-register when imported
  2. Extensible: Easy to add new providers without modifying core
  3. Lazy Loading: Optional dependencies only loaded when needed
  4. Explicit Control: Users can force specific providers when needed
  5. Pattern Priority: All patterns have equal priority (0) by default

Common Issues

Provider Not Found

python
InferenceConfigError: No provider registered for model_id='unknown-model'

Solution: Check available patterns with langextract.providers.router.list_entries()

Plugin Not Loading

python
# Your plugin isn't being discovered

Solutions:

  1. Manually trigger loading: lx.providers.load_plugins_once()
  2. Check entry points are installed: pip show -f your-package
  3. Verify no typos in pyproject.toml entry point
  4. Ensure package is installed: pip list | grep your-package

Missing Dependencies

python
InferenceConfigError: OpenAI provider requires openai package

Solution: Install optional dependencies: pip install langextract[openai]

Schema Not Working

python
# Schema constraints not being applied

Solutions:

  1. Ensure provider implements get_schema_class()
  2. Check use_schema_constraints=True is set
  3. Verify schema's requires_raw_output returns True
  4. Test schema creation with Schema.from_examples(examples)

Pattern Conflicts

python
# Multiple providers match the same model_id

Solution: Use explicit provider selection:

python
config = lx.factory.ModelConfig(
    model_id="model-name",
    provider="YourProviderClass",  # Explicit selection
)

Overview (`skills/langextract-usage/README.md`)

langextract-usage โ€” Agent Skill

This directory is an Agent Skill source that teaches
AI coding assistants how to use LangExtract correctly. It follows the open
Agent Skills format (SKILL.md with YAML frontmatter, optional
references/, examples/).

This directory is not auto-discovered by any tool from the skills/
path it lives in here. To use it, copy or symlink the directory into
whichever path your Agent-Skills-compatible tool reads from.

Activate

Pick your tool, then install at either project scope (this repo only) or
user scope (all your projects). Paths below are canonical examples per
each tool's 2026 docs โ€” some tools (Copilot, Codex) scan several paths,
so any of the documented locations works. Check the linked docs for the
full list if your tool has moved.

Project-scope paths are relative to your workspace/repo root. Run the copy
or symlink command from there.

Tool Project-scope path (example) User-scope path (example)
Google Antigravity .agents/skills/langextract-usage/ ~/.gemini/antigravity/skills/langextract-usage/
Anthropic Claude Code .claude/skills/langextract-usage/ ~/.claude/skills/langextract-usage/
OpenAI Codex .agents/skills/langextract-usage/ ~/.agents/skills/langextract-usage/
GitHub Copilot .github/skills/langextract-usage/ ~/.copilot/skills/langextract-usage/

Notes:

  • Antigravity also recognizes legacy .agent/skills/ (singular) for
    backward compatibility; new installs should use .agents/skills/
    (plural).
  • Copilot also accepts .claude/skills/ or .agents/skills/ at project
    scope, and ~/.claude/skills/ or ~/.agents/skills/ at user scope.
  • Codex scans .agents/skills/ from the current directory up through
    the repo root, so a project-scope install works from any subdirectory.
  • A project-scope .agents/skills/langextract-usage/ therefore activates
    the skill in Antigravity, Codex, and Copilot simultaneously.

Copy (static install)

bash
cp -R skills/langextract-usage <tool-skill-path>
bash
ln -s "$(pwd)/skills/langextract-usage" <tool-skill-path>

Other tools

  • Agent-Skills-compatible tools not listed above โ€” point the tool at
    this directory as a skill source, following its own docs.
  • Tools that read their own instruction format (e.g. Cursor
    .cursor/rules/, Windsurf .windsurf/rules/, Aider CONVENTIONS.md) โ€”
    copy the relevant sections of SKILL.md / references/*.md into that
    tool's convention. The content is portable; only the activation
    mechanism differs.

After activating, restart your agent session if the tool does not
auto-detect changes.

Contents

  • SKILL.md โ€” entry point; install, basic usage, key parameters, working with
    results, common issues
  • references/providers.md โ€” Gemini, OpenAI, Ollama, ModelConfig, custom
    provider plugins
  • references/resolver-params.md โ€” fuzzy alignment tuning
  • references/prompt-validation.md โ€” PromptValidationLevel details
  • examples/ โ€” runnable scripts (basic_extraction.py,
    relationship_extraction.py, multiple_documents.py)

Maintenance

When changing user-facing LangExtract APIs (imports, lx.extract() kwargs,
resolver or validation behavior), please update the affected files in this
subtree in the same PR. If you spot drift, open an issue or PR.


Examples Batch Api Example (`docs/examples/batch_api_example.md`)

Vertex AI Batch Processing Guide

The Vertex AI Batch API offers significant cost savings (~50%) for large, non-time-critical workloads. langextract seamlessly integrates this with automatic routing, caching, and fault tolerance.

Vertex AI Batch Prediction Documentation โ†’
Quotas & Limits โ†’

Real-World Example: Processing Shakespeare

This example demonstrates how to process a large text (the first ~20 pages of Romeo and Juliet) using the Batch API. We use a small chunk size (max_char_buffer=500) to generate enough chunks to trigger batch processing.

text
/* Detailed source-code truncated for AI context efficiency. */

GCS File Structure

The library automatically creates and manages a GCS bucket for you, named:
langextract-{project}-{location}-batch

Inside this bucket, data is organized as follows:

  • Input: batch-input/{job_name}.jsonl
  • Output: batch-input/{job_name}/dest/prediction-model-{timestamp}/predictions.jsonl
  • Cache: cache/{hash}.json (Individual cached results)

Cost Optimization & Caching

LangExtract's batch processing is designed to minimize costs:

  1. Cost Efficiency: Vertex AI Batch predictions are typically ~50% cheaper than online predictions.
  2. Smart Caching:
    • Results are cached in your GCS bucket (cache/ directory).
    • Instant Retrieval: Re-running identical prompts fetches results directly from storage, bypassing model inference.
    • Reduced Inference: You avoid paying for redundant model calls on previously processed data.
    • Lifecycle Management: Use retention_days (e.g., 30) to automatically clean up old data and manage storage usage.

Analyze Results

python
print(f"Extracted {len(results.extractions)} entities.")
print("First 5 extractions:")
for extraction in results.extractions[:5]:
    print(f"- {extraction.extraction_class}: {extraction.extraction_text}")

Sample Output

text
Extracted 767 entities.
First 5 extractions:
- character: ESCALUS
- character: MERCUTIO
- character: PARIS
- character: Page to Paris
- character: MONTAGUE

Note on batch_length: The batch_length parameter controls how many chunks are submitted in a single batch job. For optimal performance with the Batch API, set this to a high value (e.g., 1000) to process all chunks in a single job rather than multiple sequential jobs.

Key Features

1. Automatic Routing

langextract automatically switches between real-time and batch APIs based on your threshold.

  • < Threshold: Uses real-time API for immediate results.
  • >= Threshold: Uses Batch API for cost savings.

2. Fault Tolerance & Caching

Built-in GCS caching (enable_caching=True) allows you to resume interrupted jobs without re-processing completed items, saving time and cost.

3. Automated Storage

langextract handles all GCS operations automatically using a dedicated bucket (gs://langextract-{project}-{location}-batch). Note that input/output files are retained for debugging.

Tracking Job Status

To monitor progress, you can watch the log file from a separate terminal:

bash
tail -f batch_process.log

When running a batch job, langextract provides clear log feedback with a direct link to the Google Cloud Console:

text
INFO - Batch job created successfully: projects/123456789/locations/us/batchPredictionJobs/987654321
INFO - Job State: JobState.JOB_STATE_PENDING
INFO - Job Console URL: https://console.cloud.google.com/vertex-ai/jobs/batch-predictions/987654321?project=123456789
INFO - Batch job is running... (State: JOB_STATE_PENDING)
INFO - Batch job is running... (State: JOB_STATE_RUNNING)
  • Completion: Once the job succeeds, langextract automatically downloads, parses, and aligns the results.

Examples Japanese Extraction (`docs/examples/japanese_extraction.md`)

Japanese Information Extraction

This example demonstrates how to use LangExtract to extract structured information from Japanese text.

Note: For non-spaced languages like Japanese, use UnicodeTokenizer to ensure correct character-based segmentation and alignment.

Full Pipeline Example

text
/* Detailed source-code truncated for AI context efficiency. */

Examples Longer Text Example (`docs/examples/longer_text_example.md`)

*Romeo and Juliet* Full Text Extraction

LangExtract can process entire documents directly from URLs, handling large texts with high accuracy through parallel processing and enhanced sensitivity features. This example demonstrates extraction from the complete text of Romeo and Juliet from Project Gutenberg.

Example code

The following code uses a comprehensive prompt and examples optimized for large, complex literary texts. For large complex inputs, using more detailed examples is suggested to increase extraction robustness.

Warning: Running this example processes a large document (~44 000 tokens) and will incur costs. For large-scale use, a Tier 2 Gemini quota is suggested to avoid rate-limit issues (details). Please review the Gemini API pricing before proceeding.

text
/* Detailed source-code truncated for AI context efficiency. */

This creates an interactive HTML visualization for exploring the extracted entities:

python
# Analyze character mentions
characters = {}
for e in result.extractions:
    if e.extraction_class == "character":
        char_name = e.extraction_text
        if char_name not in characters:
            characters[char_name] = {"count": 0, "attributes": set()}
        characters[char_name]["count"] += 1
        if e.attributes:
            for attr_key, attr_val in e.attributes.items():
                characters[char_name]["attributes"].add(f"{attr_key}: {attr_val}")

# Print character summary
print(f"\nCHARACTER SUMMARY ({len(characters)} unique characters)")
print("=" * 60)

sorted_chars = sorted(characters.items(), key=lambda x: x[1]["count"], reverse=True)
for char_name, char_data in sorted_chars[:10]:  # Top 10 characters
    attrs_preview = list(char_data["attributes"])[:3]
    attrs_str = f" ({', '.join(attrs_preview)})" if attrs_preview else ""
    print(f"{char_name}: {char_data['count']} mentions{attrs_str}")

# Entity type breakdown
entity_counts = Counter(e.extraction_class for e in result.extractions)
print(f"\nENTITY TYPE BREAKDOWN")
print("=" * 60)
for entity_type, count in entity_counts.most_common():
    percentage = (count / len(result.extractions)) * 100
    print(f"{entity_type}: {count} ({percentage:.1f}%)")

Sample output

text
Downloading and processing Romeo and Juliet from Project Gutenberg...
Downloaded 147,843 characters (25,976 words) from 1513-0.txt
Extracted 4,088 entities from 147,843 characters
Interactive visualization saved to romeo_juliet_visualization.html

CHARACTER SUMMARY (153 unique characters)
============================================================
ROMEO: 287 mentions (emotional_state: excitement, emotional_state: eager to please)
JULIET: 204 mentions (emotional_state: fond, emotional_state: resilient)
NURSE: 168 mentions (emotional_state: reporting, emotional_state: teasing and evasive)
MERCUTIO: 107 mentions (emotional_state: approving, emotional_state: responsive)
BENVOLIO: 82 mentions (emotional_state: cautious, emotional_state: teasing)

ENTITY TYPE BREAKDOWN
============================================================
character: 1,685 (41.2%)
emotion: 1,524 (37.3%)
relationship: 879 (21.5%)

Key benefits for long documents

Sequential extraction passes

Multiple extraction passes improve recall by performing independent extractions and merging non-overlapping results. Each pass uses identical parameters and processingโ€”they are independent runs of the same extraction task. The number of passes is controlled by the extraction_passes parameter (e.g., extraction_passes=3).

How it works: Each pass processes the full text independently using the same prompt and examples. Results are then merged using a "first-pass wins" strategy for overlapping entities, while adding unique non-overlapping entities from later passes. This approach captures entities that might be missed in any single run due to the stochastic nature of language model generation.

Portable and Interoperable Data with JSONL

LangExtract uses JSONL, a human-readable format ideal for language model data. Each line is a self-contained JSON object, making outputs easy to parse, share, and integrate with other tools. You can save results with lx.io.save_annotated_documents and reload them for later analysis, ensuring your data is both portable and persistent.

Optimal long context management

While single-inference approaches can be powerful, their accuracy may be affected by distant context. LangExtract uses smart chunking strategies that respect text delimiters (such as paragraph breaks) to keep context intact and well-formed for the model. Users can configure context sizes (max_char_buffer) combined with parallel processing (max_workers) to maintain extraction quality across large documents. Multiple sequential extraction passes further enhance sensitivity by capturing entities that might be missed in any single run due to the stochastic nature of language model generation.

Enhanced accuracy through chunking

The chunked processing approach can improve extraction quality over a single inference pass on a large document because each chunk uses a smaller, more manageable context size. This helps the model focus on the most relevant information and prevents interference from distant context. While the overall latency and time required remain similar due to parallelization, the extraction quality can be substantially higher with better entity coverage and more accurate attribute assignment across the entire document.ยน

Interactive visualization at scale

Seamlessly explore hundreds or thousands of entities through interactive HTML visualizations generated directly from JSONL output files. The generated visualizations handle large result sets efficiently, providing navigation and detailed entity inspection capabilities for comprehensive analysis of complex documents.

Schema-guided knowledge extraction

LangExtract combines precise text positioning with world knowledge enrichment, enabling extraction of information not explicitly stated in the text (like character identities and traits). Under the hood, the library implements Controlled Generation with supported models to ensure extracted data adheres to your specified schema while maintaining robust extractions across large inputs.


ยน Models like Gemini 1.5 Pro show strong performance on many benchmarks, but needle-in-a-haystack tests across million-token contexts indicate that performance can vary in multi-fact retrieval scenarios. This demonstrates how LangExtract's smaller context windows approach ensures consistently high quality across entire documents by avoiding the complexity and potential degradation of massive single-context processing.


Examples Medication Examples (`docs/examples/medication_examples.md`)

Medication Extraction Examples

LangExtract excels at extracting structured medical information from clinical text, making it particularly useful for healthcare applications. The methodology originated from research in medical information extraction, where early versions of the techniques were demonstrated to accelerate annotation tasks significantly.

Disclaimer: This demonstration is only for illustrative purposes of LangExtract's baseline capability. It does not represent a finished or approved product, is not intended to diagnose or suggest treatment of any disease or condition, and should not be used for medical advice.


Medical Information Extraction Research:
The concepts and methods underlying LangExtract were first demonstrated in:

Goel, A., Lehman, E., Gulati, A., Chen, R., Nori, H., Hager, G. D., & Durr, N. J. (2023).
"LLMs Accelerate Annotation for Medical Information Extraction."
Machine Learning for Health (ML4H), PMLR, 2023.
arXiv:2312.02296


Basic Named Entity Recognition (NER)

In this basic medical example, LangExtract extracts structured medication information:

text
/* Detailed source-code truncated for AI context efficiency. */

This will produce an output similar to:

text
Input: Patient took 400 mg PO Ibuprofen q4h for two days.

Extracted entities:
โ€ข Dosage: 400 mg (pos: 13-19)
โ€ข Route: PO (pos: 20-22)
โ€ข Medication: Ibuprofen (pos: 23-32)
โ€ข Frequency: q4h (pos: 33-36)
โ€ข Duration: for two days (pos: 37-49)
Interactive visualization saved to medical_ner_visualization.html

The interactive HTML visualization allows you to explore the extracted entities visually, with each entity type color-coded and clickable for detailed inspection.

Relationship Extraction (RE)

For more complex extractions that involve relationships between entities, LangExtract can also extract structured relationships. This example shows how to extract medications and their associated attributes:

text
/* Detailed source-code truncated for AI context efficiency. */

This will produce output similar to:

text
Input text: The patient was prescribed Lisinopril and Metformin last month.
He takes the Lisinopril 10mg daily for hypertension, but often misses
his Metformin 500mg dose which should be taken twice daily for diabetes.

Extracted Medications:

* Lisinopril
  โ€ข Medication: Lisinopril (pos: 28-38)
  โ€ข Dosage: 10mg (pos: 89-93)
  โ€ข Frequency: daily (pos: 94-99)
  โ€ข Condition: hypertension (pos: 104-116)

* Metformin
  โ€ข Medication: Metformin (pos: 43-52)
  โ€ข Dosage: 500mg (pos: 149-154)
  โ€ข Frequency: twice daily (pos: 182-193)
  โ€ข Condition: diabetes (pos: 198-206)
Interactive visualization saved to medical_relationship_visualization.html

The visualization highlights how the medication_group attributes connect related entities, making it easy to see which dosages, frequencies, and conditions belong to each medication. Each medication group is visually distinguished in the interactive display.

Understanding Relationship Extraction:
This example demonstrates how attributes enable efficient relationship extraction. Using the medication_group attribute as a linking key, related entities are grouped together logically. This approach simplifies extracting connected information and eliminates the need for additional processing steps, while preserving the precise alignment between extracted text and its original location in the document. The interactive visualization makes these relationships immediately apparent, with connected entities sharing visual groupings and color coding.

Key Features Demonstrated

  • Named Entity Recognition: Extracts entities with their types (medication, dosage, route, etc.)
  • Relationship Extraction: Groups related entities using attributes
  • Position Tracking: Records exact positions of extracted entities in the source text
  • Structured Output: Organizes information in a format suitable for healthcare applications
  • Interactive Visualization: Generates HTML visualizations for exploring complex medical extractions with entity groupings and relationships clearly displayed

Examples Output Schema (`docs/examples/output_schema.md`)

Custom Output Schema Example

LangExtract usually derives provider schema constraints from examples. For
advanced cases, pass output_schema to constrain the raw model output more
directly. This example restricts a status attribute to the enum values
present and absent.

Examples are optional when output_schema is provided. When examples are
included, they still guide the prompt; output_schema replaces only the
provider schema constraint. The schema must describe LangExtract's JSON output
envelope with a top-level extractions array.

Gemini and OpenAI support output_schema. Ollama does not currently support
user-provided output schemas.

The helper emits additionalProperties: False so schemas work with OpenAI
strict structured outputs. Gemini receives user-provided schemas through its
native JSON Schema field, so JSON Schema keywords such as
additionalProperties are preserved.

Full Pipeline Example

text
/* Detailed source-code truncated for AI context efficiency. */

This will produce output similar to:

text
Input: Patient has hypertension. Patient denies diabetes.

Extracted conditions:
โ€ข hypertension: present
โ€ข diabetes: absent

Multiple Extraction Classes

For heterogeneous extraction classes, pass multiple item schemas. The helper
wraps them in anyOf under extractions.items:

python
output_schema = lx.schema.extractions_schema(
    lx.schema.extraction_item_schema("condition"),
    lx.schema.extraction_item_schema("medication"),
)

Raw Schema Equivalent

For full control, pass a raw JSON schema dictionary. When targeting OpenAI
strict mode, every object schema must declare required fields and
additionalProperties: False.

Attribute objects use the <extraction_class>_attributes property name.
LangExtract's resolver expects that suffix when parsing raw model output.
Each extraction item should use extraction-class text keys such as
condition; generic fields such as extraction_class, extraction_text,
and attributes are not resolver output keys. Extraction class names ending
in _attributes are reserved for attribute objects.

The full pipeline example above produces this equivalent envelope:

python
output_schema = {
    "type": "object",
    "properties": {
        "extractions": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "condition": {"type": "string"},
                    "condition_attributes": {
                        "type": "object",
                        "properties": {
                            "status": {
                                "type": "string",
                                "enum": ["present", "absent"],
                            }
                        },
                        "required": ["status"],
                        "additionalProperties": False,
                    },
                },
                "required": ["condition", "condition_attributes"],
                "additionalProperties": False,
            },
        }
    },
    "required": ["extractions"],
    "additionalProperties": False,
}

Use raw schemas when you need JSON Schema constructs that the helpers do not
cover directly, such as custom anyOf variants. OpenAI strict structured
outputs support anyOf; use strict=False only in lower-level provider code
if you need to experiment with schema features outside OpenAI's strict subset.

Optional Attributes

The helper marks every supplied attribute as required by default so the schema
is compatible with OpenAI strict structured outputs. To allow an attribute to
be absent in practice, make the value nullable while keeping the key required:

python
output_schema = lx.schema.extractions_schema(
    lx.schema.extraction_item_schema(
        "condition",
        attributes={
            "status": {
                "anyOf": [
                    {"type": "string", "enum": ["present", "absent"]},
                    {"type": "null"},
                ]
            }
        },
    )
)

If you need a schema where an attribute key may be omitted entirely, use a raw
schema for that provider-specific shape.

Errors and Pitfalls

  • Invalid envelopes raise InferenceConfigError before provider construction.
  • output_schema can be passed with either model_id/config or a
    preconfigured model when the provider supports user schemas.
  • Examples are optional with output_schema. When supplied, keep example
    classes and attribute names aligned with the schema to avoid confusing the
    model.
  • output_schema requires raw JSON provider output. Leave format_type unset
    or set it to lx.data.FormatType.JSON, and do not force fences.
  • Keep the resolver's default "_attributes" suffix. Custom
    attribute_suffix/extraction_attributes_suffix settings are incompatible
    with the raw schema envelope.
  • Do not combine output_schema with provider schema kwargs such as
    response_format, response_schema, or response_json_schema.
  • When targeting Gemini 2.0 models, add Gemini's propertyOrdering keyword
    to object schemas that need an explicit property order. The LangExtract
    helpers stay provider-neutral and do not add that Gemini-specific extension.
  • Raw schemas must describe extractions.items inline, including each
    extraction text key and <extraction_class>_attributes object. LangExtract
    does not resolve $ref for those resolver keys before provider construction.
  • Use anyOf, not oneOf, for item unions. Gemini treats oneOf like
    anyOf, and OpenAI strict structured outputs reject oneOf.
  • lx.schema.extraction_item_schema(..., additional_properties=False) applies
    that setting to both the outer extraction item object and its nested
    <extraction_class>_attributes object.
  • OpenAI uses strict structured outputs by default with LangExtract's default
    schema name. The lower-level OpenAISchema.from_schema_dict(..., schema_name=..., strict=False) constructor is an escape hatch for callers
    configuring provider models directly.
  • LangExtract validates only the output envelope locally; the provider API
    validates the JSON schema itself. OpenAI strict mode requires every object
    to list all properties in required and set
    additionalProperties: false โ€” the lx.schema helpers emit compliant
    schemas, and the OpenAI API reports the exact path of any violation in
    hand-written schemas. Schema size/depth limits, enum limits, and keyword
    support also vary by provider, model, and endpoint.
  • Avoid stop/stop_sequences with output_schema: stop sequences can
    truncate schema-constrained JSON mid-document while the response still
    reports a normal finish reason.

--- METRICS ---