Why Async? Process 100 documents in the time it takes to process 10 synchronously. Essential for high-volume applications.
Getting Started with Async
Your First Async Parse
import asyncio
from cerevox import AsyncLexa
async def main():
async with AsyncLexa() as client: # Uses CEREVOX_API_KEY
documents = await client.parse("document.pdf")
print(f"✅ Async parsing complete: {len(documents[0].content)} chars")
# Run it
asyncio.run(main())
import asyncio
from cerevox import AsyncLexa
async def main():
async with AsyncLexa() as client:
# Process multiple files concurrently - much faster!
documents = await client.parse([
"contract.pdf",
"invoice.xlsx",
"report.docx"
])
print(f"✅ Processed {len(documents)} documents concurrently")
for i, doc in enumerate(documents, 1):
print(f" 📄 Doc {i}: {len(doc.content)} chars, {len(doc.tables)} tables")
asyncio.run(main())
import asyncio
from cerevox import AsyncLexa
async def process_document_batch(files):
async with AsyncLexa() as client:
# Process large batches efficiently
documents = await client.parse(
files,
timeout=300.0, # 5 minute timeout for large batches
poll_interval=5.0 # Check status every 5 seconds
)
return documents
# Process 50+ documents efficiently
files = [f"documents/doc_{i}.pdf" for i in range(50)]
documents = asyncio.run(process_document_batch(files))
print(f"✅ Processed {len(documents)} documents in one batch")
Real-World Performance Examples
High-Volume Document Processing
import asyncio
from cerevox import AsyncLexa, ProcessingMode
async def process_financial_documents():
"""Process hundreds of financial documents efficiently"""
# Financial documents that need processing
financial_docs = [
"invoices/batch_1/*.pdf", # 100+ invoices
"statements/q1_2024/*.pdf", # Bank statements
"contracts/2024/*.docx", # Legal contracts
"reports/financial/*.xlsx" # Financial reports
]
async with AsyncLexa() as client:
# Process all document types concurrently
start_time = asyncio.get_event_loop().time()
documents = await client.parse(
financial_docs,
mode=ProcessingMode.ADVANCED, # More accurate but slower for financial data
timeout=600.0 # 10 minute timeout for large batches
)
processing_time = asyncio.get_event_loop().time() - start_time
print(f"✅ Processed {len(documents)} financial documents")
print(f"⚡ Processing time: {processing_time:.2f} seconds")
print(f"📊 Average: {processing_time/len(documents):.2f} seconds per document")
# Extract structured financial data
total_tables = sum(len(doc.tables) for doc in documents)
print(f"💰 Extracted {total_tables} financial tables")
return documents
# Process financial documents at scale
documents = asyncio.run(process_financial_documents())
import asyncio
from cerevox import AsyncLexa
async def analyze_research_papers():
"""Process academic papers for research analysis"""
papers = [
"papers/ai_research_2024/*.pdf",
"papers/machine_learning/*.pdf",
"papers/data_science/*.pdf"
]
async with AsyncLexa() as client:
# Process academic papers concurrently
documents = await client.parse(papers)
# Get research-ready chunks
all_chunks = []
for doc in documents:
chunks = doc.get_text_chunks(
target_size=800, # Larger chunks for research
overlap_size=100, # More overlap for context
preserve_citations=True # Keep academic citations
)
all_chunks.extend(chunks)
print(f"📚 Processed {len(documents)} research papers")
print(f"🔍 Generated {len(all_chunks)} research chunks")
print(f"📊 Found {sum(len(doc.tables) for doc in documents)} data tables")
return documents, all_chunks
# Analyze research at scale
documents, chunks = asyncio.run(analyze_research_papers())
RAG System Document Processing
import asyncio
from cerevox import AsyncLexa
async def build_knowledge_base():
"""Process documents for RAG knowledge base"""
knowledge_docs = [
"knowledge_base/product_docs/*.pdf",
"knowledge_base/user_manuals/*.docx",
"knowledge_base/faqs/*.html",
"knowledge_base/support_articles/*.md"
]
async with AsyncLexa() as client:
# Process all knowledge base documents
documents = await client.parse(knowledge_docs)
# Generate RAG-optimized chunks
rag_chunks = []
for doc in documents:
chunks = doc.get_text_chunks(
target_size=500, # Perfect for embeddings
overlap_size=50, # Prevent context loss
include_metadata=True # Rich metadata for retrieval
)
rag_chunks.extend(chunks)
print(f"📚 Processed knowledge base: {len(documents)} documents")
print(f"🔗 Generated {len(rag_chunks)} RAG chunks")
print(f"💾 Ready for vector database: {sum(len(chunk.content) for chunk in rag_chunks)} total characters")
# Each chunk is ready for your vector database
return rag_chunks
# Build your RAG knowledge base
rag_chunks = asyncio.run(build_knowledge_base())
# Ready for vector database insertion
print(f"✅ {len(rag_chunks)} chunks ready for embedding and storage")
import asyncio
from cerevox import AsyncLexa
async def process_multi_source_rag():
"""Process documents from multiple sources for comprehensive RAG"""
async def process_source(client, source_name, files):
"""Process documents from a specific source"""
documents = await client.parse(files)
# Tag chunks with source information
chunks = []
for doc in documents:
doc_chunks = doc.get_text_chunks(target_size=500)
for chunk in doc_chunks:
chunk.metadata['source_system'] = source_name
chunks.append(chunk)
return chunks
async with AsyncLexa() as client:
# Process multiple sources concurrently
tasks = [
process_source(client, "documentation", ["docs/*.pdf"]),
process_source(client, "support", ["support/*.docx"]),
process_source(client, "knowledge", ["kb/*.html"]),
process_source(client, "training", ["training/*.pdf"])
]
# Wait for all sources to complete
source_results = await asyncio.gather(*tasks)
# Combine all chunks
all_chunks = []
for chunks in source_results:
all_chunks.extend(chunks)
print(f"🔗 Multi-source RAG ready: {len(all_chunks)} chunks")
print(f"📊 Sources processed: {len(source_results)}")
return all_chunks
# Build comprehensive RAG system
rag_chunks = asyncio.run(process_multi_source_rag())
Controlled Concurrency Patterns
Production-Grade Concurrency Control
import asyncio
from cerevox import AsyncLexa, LexaError
async def process_with_concurrency_limit(files, max_concurrent=5):
"""Process files with controlled concurrency - prevents overwhelming the API"""
semaphore = asyncio.Semaphore(max_concurrent)
results = []
async def process_single_file(client, file):
async with semaphore: # Limit concurrent operations
try:
documents = await client.parse([file])
print(f"✅ Processed: {file}")
return documents[0] if documents else None
except LexaError as e:
print(f"❌ Failed {file}: {e.message}")
return None
async with AsyncLexa() as client:
# Create tasks for all files
tasks = [process_single_file(client, file) for file in files]
# Process with controlled concurrency
completed_results = await asyncio.gather(*tasks, return_exceptions=True)
# Filter successful results
successful_docs = [r for r in completed_results if r and not isinstance(r, Exception)]
print(f"✅ Successfully processed {len(successful_docs)}/{len(files)} files")
return successful_docs
# Process large document sets safely
files = [f"documents/batch_{i}.pdf" for i in range(100)]
documents = asyncio.run(process_with_concurrency_limit(files, max_concurrent=10))
import asyncio
from cerevox import AsyncLexa, LexaError
async def robust_batch_processing(all_files, batch_size=20):
"""Process files in batches with error recovery"""
# Split into batches
batches = [all_files[i:i + batch_size] for i in range(0, len(all_files), batch_size)]
async def process_batch_with_retry(client, batch, batch_num, max_retries=3):
for attempt in range(max_retries):
try:
print(f"🔄 Processing batch {batch_num} (attempt {attempt + 1})")
documents = await client.parse(batch, timeout=300.0)
print(f"✅ Batch {batch_num} complete: {len(documents)} documents")
return documents
except LexaError as e:
if attempt < max_retries - 1:
wait_time = 2 ** attempt # Exponential backoff
print(f"⏳ Batch {batch_num} failed, retrying in {wait_time}s...")
await asyncio.sleep(wait_time)
else:
print(f"❌ Batch {batch_num} failed after {max_retries} attempts")
return []
return []
async with AsyncLexa() as client:
# Process batches with limited concurrency
semaphore = asyncio.Semaphore(3) # Max 3 concurrent batches
async def controlled_batch_processing(batch, batch_num):
async with semaphore:
return await process_batch_with_retry(client, batch, batch_num)
# Create tasks for all batches
tasks = [
controlled_batch_processing(batch, i + 1)
for i, batch in enumerate(batches)
]
# Process all batches
batch_results = await asyncio.gather(*tasks)
# Combine successful results
all_documents = []
for batch_docs in batch_results:
all_documents.extend(batch_docs)
print(f"🎉 Batch processing complete: {len(all_documents)} total documents")
return all_documents
# Process thousands of documents reliably
large_file_list = [f"archive/document_{i}.pdf" for i in range(1000)]
documents = asyncio.run(robust_batch_processing(large_file_list))
Advanced Async Patterns
Progress Monitoring & Real-time Updates
import asyncio
from cerevox import AsyncLexa
async def process_with_realtime_progress(files):
"""Process files with real-time progress updates"""
progress_data = {
'total': len(files),
'completed': 0,
'failed': 0,
'in_progress': 0
}
def update_progress(status, file_name):
"""Update progress based on status"""
if status == 'started':
progress_data['in_progress'] += 1
elif status == 'completed':
progress_data['in_progress'] -= 1
progress_data['completed'] += 1
elif status == 'failed':
progress_data['in_progress'] -= 1
progress_data['failed'] += 1
# Print progress bar
total = progress_data['total']
completed = progress_data['completed']
failed = progress_data['failed']
in_progress = progress_data['in_progress']
progress_pct = (completed + failed) / total * 100
print(f"\r📊 Progress: {progress_pct:.1f}% | ✅ {completed} | ❌ {failed} | 🔄 {in_progress}", end='')
async def process_single_with_progress(client, file):
update_progress('started', file)
try:
documents = await client.parse([file])
update_progress('completed', file)
return documents[0] if documents else None
except Exception as e:
update_progress('failed', file)
return None
async with AsyncLexa() as client:
# Process all files with progress tracking
tasks = [process_single_with_progress(client, file) for file in files]
results = await asyncio.gather(*tasks, return_exceptions=True)
print() # New line after progress bar
successful_docs = [r for r in results if r and not isinstance(r, Exception)]
print(f"🎉 Processing complete!")
print(f"✅ Successful: {len(successful_docs)}")
print(f"❌ Failed: {len(files) - len(successful_docs)}")
return successful_docs
# Process with real-time progress
files = [f"documents/file_{i}.pdf" for i in range(50)]
documents = asyncio.run(process_with_realtime_progress(files))
import asyncio
from asyncio import Queue
from cerevox import AsyncLexa
async def queue_based_processing(files, num_workers=5):
"""Process files using a queue with multiple workers"""
# Create queues
file_queue = Queue()
result_queue = Queue()
# Add all files to the queue
for file in files:
await file_queue.put(file)
async def worker(client, worker_id):
"""Worker function to process files from queue"""
processed = 0
while True:
try:
# Get file from queue (timeout after 1 second)
file = await asyncio.wait_for(file_queue.get(), timeout=1.0)
# Process the file
try:
documents = await client.parse([file])
await result_queue.put(('success', file, documents))
processed += 1
print(f"👤 Worker {worker_id}: processed {file} ({processed} total)")
except Exception as e:
await result_queue.put(('error', file, str(e)))
print(f"👤 Worker {worker_id}: failed {file}")
# Mark task as done
file_queue.task_done()
except asyncio.TimeoutError:
# No more files in queue, worker can exit
print(f"👤 Worker {worker_id}: completed ({processed} files processed)")
break
async with AsyncLexa() as client:
# Start worker tasks
workers = [
asyncio.create_task(worker(client, i + 1))
for i in range(num_workers)
]
# Wait for all files to be processed
await file_queue.join()
# Cancel workers
for w in workers:
w.cancel()
# Collect results
results = []
while not result_queue.empty():
results.append(await result_queue.get())
# Process results
successful_docs = []
failed_files = []
for status, file, data in results:
if status == 'success':
successful_docs.extend(data)
else:
failed_files.append((file, data))
print(f"🎯 Queue processing complete:")
print(f"✅ Successful: {len(successful_docs)}")
print(f"❌ Failed: {len(failed_files)}")
return successful_docs, failed_files
# Process with worker queue
files = [f"documents/doc_{i}.pdf" for i in range(100)]
successful_docs, failed_files = asyncio.run(queue_based_processing(files, num_workers=8))
Integration with Web Frameworks
FastAPI Integration
from fastapi import FastAPI, UploadFile, File, BackgroundTasks
from cerevox import AsyncLexa
import asyncio
app = FastAPI()
# Global client (reuse connection)
lexa_client = None
@app.on_event("startup")
async def startup_event():
global lexa_client
lexa_client = AsyncLexa()
@app.on_event("shutdown")
async def shutdown_event():
if lexa_client:
await lexa_client.close()
@app.post("/parse-documents/")
async def parse_documents(files: list[UploadFile] = File(...)):
"""Parse uploaded documents asynchronously"""
# Read file contents
file_contents = []
for file in files:
content = await file.read()
file_contents.append(content)
# Parse documents concurrently
documents = await lexa_client.parse(file_contents)
# Return structured results
results = []
for i, doc in enumerate(documents):
results.append({
'filename': files[i].filename,
'content_length': len(doc.content),
'tables': len(doc.tables),
'images': len(doc.images),
'content_preview': doc.content[:200]
})
return {
'status': 'success',
'processed': len(results),
'results': results
}
# Run with: uvicorn main:app --reload
from fastapi import FastAPI, BackgroundTasks
from cerevox import AsyncLexa
import asyncio
from typing import Dict
app = FastAPI()
processing_status: Dict[str, dict] = {}
async def background_parse_task(task_id: str, files: list):
"""Background task for processing large document batches"""
processing_status[task_id] = {
'status': 'processing',
'progress': 0,
'total': len(files),
'results': []
}
try:
async with AsyncLexa() as client:
documents = await client.parse(files)
# Store results
results = []
for doc in documents:
results.append({
'content_length': len(doc.content),
'tables': len(doc.tables),
'chunks': len(doc.get_text_chunks())
})
processing_status[task_id] = {
'status': 'completed',
'progress': 100,
'total': len(files),
'results': results
}
except Exception as e:
processing_status[task_id] = {
'status': 'error',
'error': str(e),
'progress': 0,
'total': len(files)
}
@app.post("/parse-batch/")
async def parse_batch(background_tasks: BackgroundTasks, files: list[str]):
"""Start background parsing task"""
task_id = f"task_{len(processing_status) + 1}"
# Start background processing
background_tasks.add_task(background_parse_task, task_id, files)
return {'task_id': task_id, 'status': 'started'}
@app.get("/parse-status/{task_id}")
async def get_parse_status(task_id: str):
"""Get status of parsing task"""
if task_id not in processing_status:
return {'error': 'Task not found'}
return processing_status[task_id]
Performance Tip: Async processing is 10x faster for multiple documents. Always use async in production for document batches larger than 5 files.

