Skip to main content

LLM-powered structured information extraction using DSPy optimization

Project description

LangStruct

Python 3.12+ MIT License DSPy 3.0 Docs

Extract structured data from any text – automatically optimized for any LLM

You know when you have a pile of invoices, medical records, or contracts, and you need to pull out specific fields or metadata? That's what this does.

Point it at messy text, show it an example of what you want, and get back typed JSON. Built on DSPy so it auto-optimizes the prompts for you. You can even parse user queries into filters for your RAG system.

from langstruct import LangStruct

# Just show an example of what you want
extractor = LangStruct(example={
    "invoice_number": "INV-001",
    "amount": 1250.00,
    "due_date": "2024-03-15",
})

# Extract from any similar text
result = extractor.extract("Invoice INV-2024-789 for $3,450 is due April 20th, 2024")
print(result.entities)
# {"invoice_number": "INV-2024-789", "amount": 3450.00, "due_date": "2024-04-20"}

Why use this

  • Auto-optimizes prompts – DSPy's optimizers do the tuning for you, learns from your examples
  • Type-safe with Pydantic – Full validation and type hints
  • End-to-end RAG – Extract structured metadata from docs AND parse user queries into filters
  • Shows sources – Character-level mapping back to original text
  • Works with any LLM – OpenAI, Claude, Gemini, or local models. Switch providers without rewriting code.

We built this because we got tired of writing extraction code over and over. The DSPy foundation means your extractor improves with feedback, and you're not locked into one LLM provider.

Installation

pip install langstruct

# You'll need an API key for any LLM provider
export GOOGLE_API_KEY="your-key"
# OR
export OPENAI_API_KEY="your-key"
# OR
export ANTHROPIC_API_KEY="your-key"

Works with free tier APIs, paid providers, or local models (Ollama). See docs for all options.

Basic usage

Two ways to define what you want:

Option 1: Show an example (easiest)

extractor = LangStruct(example={
    "name": "Dr. Sarah Johnson",
    "age": 34,
    "specialty": "cardiology"
})

Option 2: Use a Pydantic schema (more control)

from pydantic import BaseModel, Field

class PersonSchema(BaseModel):
    name: str = Field(description="Full name")
    age: int
    specialty: str = Field(description="Medical specialty")

extractor = LangStruct(schema=PersonSchema)

Then extract:

text = "Dr. Sarah Johnson, 34, is a cardiologist at Boston General."
result = extractor.extract(text)

print(result.entities)  # {"name": "Dr. Sarah Johnson", "age": 34, "specialty": "cardiology"}
print(result.confidence)  # 0.94

RAG integration

If you're building a RAG system, LangStruct helps with both sides:

1. Extract structured metadata from documents:

metadata_extractor = LangStruct(example={
    "company": "Apple Inc.",
    "revenue": 125.3,
    "quarter": "Q3 2024"
})

# Add to your vector DB with structured filters
metadata = metadata_extractor.extract(document).entities

2. Parse user queries into filters:

query = "Q3 2024 tech companies with revenue over $100B"
parsed = metadata_extractor.query(query)

print(parsed.semantic_terms)  # ["tech companies"]
print(parsed.structured_filters)  # {"quarter": "Q3 2024", "revenue": {"$gte": 100.0}}

# Now query your vector DB with both
results = vector_db.search(
    semantic=parsed.semantic_terms,
    filters=parsed.structured_filters
)

This gives you precise retrieval instead of just semantic search. See RAG integration guide for details.

Boost accuracy with refinement

Add refine=True to get 15-30% better accuracy. It generates multiple candidates and picks the best one:

result = extractor.extract(text, refine=True)
# Takes longer but significantly more accurate

Source tracking

See exactly where each field came from:

for field, spans in result.sources.items():
    for span in spans:
        print(f"{field}: '{span.text}' at position {span.start}-{span.end}")

This is helpful for:

  • Debugging extraction issues
  • Building citation systems
  • Compliance/audit trails

Batch processing

documents = [doc1, doc2, doc3, ...]  # Hundreds or thousands

results = extractor.extract(
    documents,
    max_workers=8,         # Parallel processing
    rate_limit=60,         # Respect API limits
    show_progress=True     # Show progress bar
)

Handles retries with exponential backoff automatically.

What it's good at

We use this for:

  • Processing invoices, receipts, purchase orders
  • Extracting data from medical records and clinical notes
  • Parsing contracts for key terms and dates
  • Structuring customer feedback and reviews
  • Pulling metrics from financial reports

What it's NOT good at

  • Real-time applications (LLM latency adds up)
  • Perfect accuracy on every extraction (it's an LLM, not a regex)
  • Complex tables with merged cells or unusual layouts
  • Documents where formatting is critical to meaning

For those cases you might want a traditional parser or OCR solution.

Comparison with alternatives

LangStruct vs LangExtract

Both are solid for structured extraction. Here's how they differ:

Feature LangStruct LangExtract
Optimization ✅ Automatic (DSPy MIPROv2) ❌ Manual prompt tuning
Refinement ✅ Best-of-N + iterative improvement ⚠️ Multi-pass extraction; no Best-of-N/judge pipeline
Schema Definition ✅ From examples OR Pydantic ⚠️ Prompt + examples (no Pydantic models)
Source Grounding ✅ Character-level tracking ✅ Character-level tracking
Confidence Scores ✅ Built-in ⚠️ Not surfaced as scores
Query Parsing ✅ Bidirectional (docs + queries) ❌ Documents only
Model Support ✅ Any LLM (via DSPy/LiteLLM) ✅ Gemini, OpenAI, local via Ollama; extensible
Learning Curve ✅ Simple (example-based) ⚠️ Requires prompt + example design
Performance ✅ Self-optimizing Depends on manual tuning
Project Type Community open-source Google open-source

Use LangStruct if you want automatic optimization and don't want to tune prompts. Use LangExtract if you prefer direct control or want Google's backing.

Advanced features

Once you've got the basics working, there's more:

Custom optimization on your data:

extractor.optimize(
    texts=your_examples,
    expected_results=expected_outputs,
    num_trials=50
)

Save and reuse extractors:

extractor.save("./my_extractor")
loaded = LangStruct.load("./my_extractor")

Visualize extractions with highlighted sources:

from langstruct import HTMLVisualizer

viz = HTMLVisualizer()
viz.save_visualization(text, result, "output.html")

Process huge documents with chunking:

from langstruct import ChunkingConfig

config = ChunkingConfig(
    max_tokens=1500,
    overlap_tokens=150,
    preserve_sentences=True
)

extractor = LangStruct(schema=YourSchema, chunking_config=config)

See examples for medical records, financial docs, legal contracts, and more.

Common issues

"No API key found"

# Make sure it's exported in your current shell
echo $GOOGLE_API_KEY

# For persistence, add to ~/.bashrc or ~/.zshrc
export GOOGLE_API_KEY="your-key"

Rate limits / costs getting too high

# Control API usage
results = extractor.extract(
    texts,
    rate_limit=30,           # Calls per minute
    max_workers=2,           # Fewer parallel calls
    refine=False             # Skip refinement to save calls
)

Low extraction quality

  • Try refine=True for better accuracy
  • Use more specific field descriptions
  • Optimize on a few examples with .optimize()

Contributing

We're open to contributions. If you find bugs or have ideas:

Development setup:

git clone https://github.com/langstruct-ai/langstruct.git
cd langstruct
uv sync --extra dev
uv run pytest

License

MIT – see LICENSE

Credits

Built on DSPy for self-optimizing prompts and Pydantic for schemas.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langstruct-0.1.4.tar.gz (419.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langstruct-0.1.4-py3-none-any.whl (75.1 kB view details)

Uploaded Python 3

File details

Details for the file langstruct-0.1.4.tar.gz.

File metadata

  • Download URL: langstruct-0.1.4.tar.gz
  • Upload date:
  • Size: 419.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for langstruct-0.1.4.tar.gz
Algorithm Hash digest
SHA256 aaf677da9f54a15ae53286571f14e2f4621f690d1364e08edeab70673440942e
MD5 b5e6847fbfc1d8a13c0a70ebba92bfb9
BLAKE2b-256 afe9ff97937c46261635b92066523439701b5ef758674df916495bf8b706c7b0

See more details on using hashes here.

File details

Details for the file langstruct-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: langstruct-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 75.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for langstruct-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 7261f6f9d62ea156c656661c5bf9ab0de5798fe99308acb3c925d9aea7722122
MD5 36025b261966afdf25d686a55216dcb0
BLAKE2b-256 942ab92d5a36391dd8c6658dc56c0a5e626548777fc08386f2a93cc7aa09ebc6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page