Technical Documentation: google/langextract
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`README.md`)
LangExtract
Table of Contents
- Introduction
- Why LangExtract?
- Quick Start
- Installation
- API Key Setup for Cloud Models
- Adding Custom Model Providers
- Using OpenAI Models
- Using Local LLMs with Ollama
- More Examples
- Community Providers
- Contributing
- Testing
- How to Cite
- Disclaimer
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?
- Precise Source Grounding: Maps every extraction to its exact location in the source text, enabling visual highlighting for easy traceability and verification.
- 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.
- 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.
- Interactive Visualization: Instantly generates a self-contained, interactive HTML file to visualize and review thousands of extracted entities in their original context.
- 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.
- 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.
- 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.
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_textshould ideally be verbatim from the example'stext(no paraphrasing), listed in order of appearance. LangExtract raisesPrompt alignmentwarnings 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.
# 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-flashis 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.
# 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:
# 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 withlanguage_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
pip install langextractRecommended for most users. For isolated environments, consider using a virtual environment:
python -m venv langextract_env
source langextract_env/bin/activate # On Windows: langextract_env\Scripts\activate
pip install langextractFrom 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.
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
docker build -t langextract .
docker run --rm -e LANGEXTRACT_API_KEY="your-api-key" langextract python your_script.pyAPI 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:
- AI Studio for Gemini models
- Vertex AI for enterprise use
- OpenAI Platform for OpenAI models
Setting up API key in your environment
Option 1: Environment Variable
export LANGEXTRACT_API_KEY="your-api-key-here"Option 2: .env File (Recommended)
Add your API key to a .env file:
# Add API key to .env file
cat >> .env << 'EOF'
LANGEXTRACT_API_KEY=your-api-key-here
EOF
# Keep your API key secure
echo '.env' >> .gitignoreIn your Python code:
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:
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:
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(...)fromlangextract.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]):
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 thelx.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.
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:
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:
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.
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.
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:
# 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 testsOr reproduce the full CI matrix locally with tox:
tox # runs pylint + pytest on Python 3.10 and 3.11Ollama Integration Testing
If you have Ollama installed locally, you can run integration tests:
# Test Ollama integration (requires Ollama running with gemma2:2b model)
tox -e ollama-integrationThis 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:
# Auto-format all code
./autoformat.sh
# Or run formatters separately
isort langextract tests --profile google --line-length 80
pyink langextract tests --config pyproject.tomlPre-commit Hooks
For automatic formatting checks:
pre-commit install # One-time setup
pre-commit run --all-files # Manual runLinting
Run linting before submitting PRs:
pylint --rcfile=.pylintrc langextract testsSee CONTRIBUTING.md for full development guidelines.
How to Cite
If you use LangExtract in your research, please cite it:
@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:
python scripts/create_provider_plugin.py MyProvider --with-schemaThis will create a complete plugin structure with all boilerplate code ready for customization.
Structure
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 scriptKey Components
Provider Implementation (`provider.py`)
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 resultsPackage Configuration (`pyproject.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
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 TrueThen in your provider:
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_schemaInstallation
# 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.pyUsage
Since this example registers the same pattern as the default Gemini provider, you must explicitly specify it:
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
# Copy this example directory
cp -r examples/custom_provider_plugin/ ~/langextract-myprovider/
# Rename the package directory
cd ~/langextract-myprovider/
mv langextract_provider_example langextract_myprovider2. 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
CustomGeminiProvidertoMyProvider - 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 inBaseSchema) based on whether your provider emits raw JSON/YAML or fenced output
5. Install and Test
# 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
# Build package
python -m build
# Upload to PyPI
twine upload dist/*Share with the community:
- Submit a PR to add your provider to the Community Providers Registry
- Open an issue on LangExtract GitHub to announce your provider and get feedback
Common Pitfalls to Avoid
- Forgetting to trigger plugin loading - Plugins load lazily, use
load_plugins_once()in tests - Pattern conflicts - Avoid patterns that conflict with built-in providers
- Missing dependencies - List all requirements in
pyproject.toml - Schema mismatches - Test schema generation with real examples
- 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
# Install and start Ollama
ollama pull gemma2:2b
ollama serve # Keep this running in a separate terminal
# Run the demo
python demo_ollama.pyOption 2: Run with Docker
# Runs both Ollama and the demo in containers
docker-compose upFiles
demo_ollama.py- Comprehensive extraction examples demonstrating Ollama on README examplesdocker-compose.yml- Production-ready Docker setup with health checksDockerfile- Container definition for LangExtract
Configuration Options
Timeout Settings
For slower models or large prompts, you may need to increase the timeout (default: 120 seconds):
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:
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:
- Gemma models: Gemma Terms of Use
- Llama models: Meta Llama License
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 BaseLanguageModelfrom langextract.inference import ScoredOutputโfrom langextract.core.types import ScoredOutputfrom langextract.inference import InferenceOutputErrorโfrom langextract.core.exceptions import InferenceOutputErrorfrom langextract.inference import GeminiLanguageModelโfrom langextract.providers.gemini import GeminiLanguageModelfrom langextract.inference import OpenAILanguageModelโfrom langextract.providers.openai import OpenAILanguageModelfrom langextract.inference import OllamaLanguageModelโfrom langextract.providers.ollama import OllamaLanguageModel
Schema Module
from langextract.schema import BaseSchemaโfrom langextract.core.schema import BaseSchemafrom langextract.schema import Constraintโfrom langextract.core.schema import Constraintfrom langextract.schema import ConstraintTypeโfrom langextract.core.schema import ConstraintTypefrom langextract.schema import EXTRACTIONS_KEYโfrom langextract.core.schema import EXTRACTIONS_KEYfrom 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:
python scripts/create_provider_plugin.py MyProvider --with-schemaArchitecture Overview
The provider system uses a router pattern with automatic discovery:
- Router (
router.py): Maps model ID patterns to provider classes. A legacyregistry.pyalias still imports โ new code should userouterdirectly. - Factory (
../factory.py): Creates provider instances based on model IDs - Providers: Implement the
BaseLanguageModelinterface (fromlangextract.core.base_model)
Provider Resolution Flow
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:
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
# 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
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 providerImportant 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=1to 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:
- Factory receives model_id: "gemini-3.5-flash"
- Router searches patterns: Each provider registers regex patterns
- First match wins: Returns the matching provider class
- Provider instantiated: With model_id and any kwargs
- Inference runs: Using the selected provider
Pattern Registration Example
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
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:
# 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
# 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
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**
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`)
[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 returningScoredOutputobjects - 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 supportsoutput_schema - Implement
to_provider_config()method - Add
get_schema_class()to provider - Handle schema in provider's
__init__()andinfer()
โ **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
Option 1: External Plugin (Recommended)
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:
pip install langextract-yourprovider
# That's it! The provider is now available in langextractFor Developers (Creating an External Plugin)
- Create a new package:
langextract-myprovider/
โโโ pyproject.toml
โโโ README.md
โโโ langextract_myprovider/
โโโ __init__.py- Configure entry point in
pyproject.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"- Implement your provider:
# 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 intoScoredOutput 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.registerdecorator 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)
- Publish your package to PyPI:
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
/* Detailed source-code truncated for AI context efficiency. */2. Update Your Provider
/* Detailed source-code truncated for AI context efficiency. */3. Schema Usage
When users set use_schema_constraints=True, LangExtract will:
- Call your provider's
get_schema_class() - Use
from_examples()to build a schema from provided examples - Call
to_provider_config()to get provider-specific kwargs - Pass these kwargs to your provider's
__init__() - Your provider uses the schema for structured output
For user-authored schemas passed as output_schema=..., also implementBaseSchema.from_schema_dict(). If the provider exposes lower-level schema
kwargs such as response_schema, list them inoutput_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 exposefrom_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 overrideapply_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.
- Create your provider file:
# 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- Import it in
providers/__init__.py:
# In langextract/providers/__init__.py
from langextract.providers import myprovider # noqa: F401- 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
- Zero Configuration: Providers auto-register when imported
- Extensible: Easy to add new providers without modifying core
- Lazy Loading: Optional dependencies only loaded when needed
- Explicit Control: Users can force specific providers when needed
- Pattern Priority: All patterns have equal priority (0) by default
Common Issues
Provider Not Found
InferenceConfigError: No provider registered for model_id='unknown-model'Solution: Check available patterns with langextract.providers.router.list_entries()
Plugin Not Loading
# Your plugin isn't being discoveredSolutions:
- Manually trigger loading:
lx.providers.load_plugins_once() - Check entry points are installed:
pip show -f your-package - Verify no typos in
pyproject.tomlentry point - Ensure package is installed:
pip list | grep your-package
Missing Dependencies
InferenceConfigError: OpenAI provider requires openai packageSolution: Install optional dependencies: pip install langextract[openai]
Schema Not Working
# Schema constraints not being appliedSolutions:
- Ensure provider implements
get_schema_class() - Check
use_schema_constraints=Trueis set - Verify schema's
requires_raw_outputreturnsTrue - Test schema creation with
Schema.from_examples(examples)
Pattern Conflicts
# Multiple providers match the same model_idSolution: Use explicit provider selection:
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, optionalreferences/, 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)
cp -R skills/langextract-usage <tool-skill-path>Symlink (tracks this repo's updates)
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/, AiderCONVENTIONS.md) โ
copy the relevant sections ofSKILL.md/references/*.mdinto 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 issuesreferences/providers.mdโ Gemini, OpenAI, Ollama,ModelConfig, custom
provider pluginsreferences/resolver-params.mdโ fuzzy alignment tuningreferences/prompt-validation.mdโPromptValidationLeveldetailsexamples/โ 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.
/* 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:
- Cost Efficiency: Vertex AI Batch predictions are typically ~50% cheaper than online predictions.
- 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.
- Results are cached in your GCS bucket (
Analyze Results
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
Extracted 767 entities.
First 5 extractions:
- character: ESCALUS
- character: MERCUTIO
- character: PARIS
- character: Page to Paris
- character: MONTAGUENote on
batch_length: Thebatch_lengthparameter 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:
tail -f batch_process.logWhen running a batch job, langextract provides clear log feedback with a direct link to the Google Cloud Console:
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,
langextractautomatically 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
UnicodeTokenizerto ensure correct character-based segmentation and alignment.
Full Pipeline Example
/* 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.
/* Detailed source-code truncated for AI context efficiency. */This creates an interactive HTML visualization for exploring the extracted entities:
# 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
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:
/* Detailed source-code truncated for AI context efficiency. */This will produce an output similar to:
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.htmlThe 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:
/* Detailed source-code truncated for AI context efficiency. */This will produce output similar to:
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.htmlThe 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 valuespresent 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 asadditionalProperties are preserved.
Full Pipeline Example
/* Detailed source-code truncated for AI context efficiency. */This will produce output similar to:
Input: Patient has hypertension. Patient denies diabetes.
Extracted conditions:
โข hypertension: present
โข diabetes: absentMultiple Extraction Classes
For heterogeneous extraction classes, pass multiple item schemas. The helper
wraps them in anyOf under extractions.items:
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 andadditionalProperties: 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 ascondition; 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:
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:
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
InferenceConfigErrorbefore provider construction. output_schemacan be passed with eithermodel_id/configor a
preconfiguredmodelwhen 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_schemarequires raw JSON provider output. Leaveformat_typeunset
or set it tolx.data.FormatType.JSON, and do not force fences.- Keep the resolver's default
"_attributes"suffix. Customattribute_suffix/extraction_attributes_suffixsettings are incompatible
with the raw schema envelope. - Do not combine
output_schemawith provider schema kwargs such asresponse_format,response_schema, orresponse_json_schema. - When targeting Gemini 2.0 models, add Gemini's
propertyOrderingkeyword
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.itemsinline, including each
extraction text key and<extraction_class>_attributesobject. LangExtract
does not resolve$reffor those resolver keys before provider construction. - Use
anyOf, notoneOf, for item unions. Gemini treatsoneOflikeanyOf, and OpenAI strict structured outputs rejectoneOf. lx.schema.extraction_item_schema(..., additional_properties=False)applies
that setting to both the outer extraction item object and its nested<extraction_class>_attributesobject.- OpenAI uses strict structured outputs by default with LangExtract's default
schema name. The lower-levelOpenAISchema.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 inrequiredand setadditionalProperties: falseโ thelx.schemahelpers 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_sequenceswithoutput_schema: stop sequences can
truncate schema-constrained JSON mid-document while the response still
reports a normal finish reason.
--- METRICS ---
- Files Extracted: 11
- Estimated Token Budget: ~19500 tokens
- Canonical Reference: https://codewiki.google/github.com/google/langextract