Async Operations¶
structx uses LiteLLM's asynchronous completion API through an Instructor
AsyncInstructor. Planning and row extraction are native async model calls.
Blocking file parsing and document conversion are the only operations moved to
a worker thread.
Extractor.from_litellm configures both clients automatically. When creating
Extractor directly, pass both client and async_client; async methods fail
fast when no async Instructor client is configured.
Basic Async Extraction¶
import asyncio
async def extract_data():
result = await extractor.extract_async(
data="scripts/example_input/free-consultancy-agreement.docx",
query="extract the main parties and the effective date"
)
return result
# Run the async function
result = asyncio.run(extract_data())
Async Methods¶
For each synchronous method, there is an async counterpart:
| Synchronous Method | Asynchronous Method |
|---|---|
prepare_input |
prepare_input_async |
extract |
extract_async |
extract_queries |
extract_queries_async |
get_schema |
get_schema_async |
refine_data_model |
refine_data_model_async |
Explicit preparation can be inspected and reused without repeating document conversion:
async with extractor.prepare_input_async(data="agreement.docx") as prepared:
result = await extractor.extract_async(
data=prepared,
query="extract agreement terms",
model=AgreementTerms,
)
Parallel Processing¶
Process multiple documents in parallel:
Async Processing Flow¶
View Async Processing Flow Diagram
graph LR
A[Prepare Input] --> B[Plan Model Once]
B --> C[Create Row Tasks]
C --> D[Async Semaphore]
D --> E1[Row 1 Request]
D --> E2[Row 2 Request]
D --> E3[Row N Request]
E1 --> F[Stable Row Outcomes]
E2 --> F
E3 --> F
F --> G[Result Collection]
import asyncio
async def process_documents(docs):
tasks = []
for doc_path, query in docs.items():
task = extractor.extract_async(
data=doc_path,
query=query
)
tasks.append(task)
results = await asyncio.gather(*tasks)
return results
documents = {
"scripts/example_input/free-consultancy-agreement.docx": "extract parties and governing law",
"scripts/example_input/S0305SampleInvoice.pdf": "extract invoice number and total amount"
}
results = asyncio.run(process_documents(documents))
Combining with Other Async Operations¶
import asyncio
async def fetch_and_extract(fetch_text, query):
content = await fetch_text()
return await extractor.extract_async(
data=[{"text": content}],
query=query
)
Async Multiple Queries¶
import asyncio
async def process_multiple_queries():
queries = [
"extract all clauses related to payment",
"extract termination conditions",
"summarize the scope of services"
]
results = await extractor.extract_queries_async(
data="scripts/example_input/free-consultancy-agreement.docx",
queries=queries
)
return results
results = asyncio.run(process_multiple_queries())
extract_queries_async prepares the input once and processes queries
sequentially. Rows within each query are concurrent. Keeping queries sequential
prevents query concurrency from multiplying row concurrency unexpectedly.
Row Concurrency¶
Each row remains an independent model request. max_threads is also the maximum
number of in-flight async row requests for one extraction operation, while
batch_size limits how many row tasks are scheduled at once. Results are stored
by input position, so completion order does not affect output order or failure
attribution. Each result.rows entry retains its own usage object; the top-level
usage is merged in stable input order.
Rows are intentionally not combined into one prompt. Combining them would require token-aware packing, generated row identifiers, partial-response validation, and retrying only failed members of a combined call. It also lets one oversized or malformed row invalidate unrelated rows. Independent requests provide predictable limits, exact usage per call, and isolated retries.
Use separate extract_async tasks with asyncio.gather for independent
documents. Be aware that each operation has its own max_threads allowance.
Best Practices¶
- Use in Async Environments: Only use async methods in async environments
- Limit Concurrency: Set
max_threadsto match provider rate limits - Handle Errors: Use try/except with async operations
- Close Resources: Ensure proper cleanup of resources in async contexts
Next Steps¶
- Check out the API Reference for detailed method signatures
- Try Model Refinement to enhance your data models
- Explore Token Usage Tracking to monitor resource consumption
- Learn about retry mechanisms for robust error handling
- See Examples for real-world use cases