Type-safe LLM output parsing with Pydantic models and Jinja2 prompt templates
Project description
llmcast
A lightweight Python library for structured LLM output parsing. Define prompts as typed Pydantic templates, get back validated Python objects — with retries, token tracking, and support for both sync and async workflows.
Requirements: Python 3.13+
Installation
pip install llmcast
Core concepts
BaseTemplate — typed prompt
A prompt is a Pydantic model whose docstring is a Jinja2 template. Fields become template variables and are automatically rendered on str(). The full Jinja2 syntax is available — conditionals, loops, filters, etc.
from llmcast.template import BaseTemplate
class ExtractCompanyInfo(BaseTemplate):
"""
Extract company information from the text below.
Respond in {{ output_format }} format.
Text: {{ text }}
"""
text: str
The built-in output_format field ("json" by default) can be referenced in the template to instruct the model which format to use.
SyncLLMParser / AsyncLLMParser — parsers
Wrap an OpenAI-compatible client and call .parse() to get a validated Pydantic object back.
from openai import OpenAI
from pydantic import BaseModel
from llmcast.parser.sync import SyncLLMParser
class CompanyInfo(BaseModel):
name: str
founded: int
employees: int
client = OpenAI()
parser = SyncLLMParser(client, "gpt-4o")
result = parser.parse(
prompt=ExtractCompanyInfo(text="Anthropic was founded in 2021 and has ~500 employees."),
result_schema=CompanyInfo,
)
if result:
company, usage = result
print(company.name) # Anthropic
print(usage.total_tokens) # 142
Async usage with concurrency control:
import asyncio
from openai import AsyncOpenAI
from llmcast.parser.async_ import AsyncLLMParser
parser = AsyncLLMParser(AsyncOpenAI(), "gpt-4o", concurrency_limit=5)
results = await asyncio.gather(*[
parser.parse(ExtractCompanyInfo(text=text), CompanyInfo)
for text in texts
])
Structured output vs. text parsing
By default (structured_output=True) the parser uses OpenAI's native structured output API (response_format), which guarantees schema-valid JSON. For providers or models that don't support this, set structured_output=False — the parser will instead extract and validate output from the raw text response.
# Provider doesn't support structured output — parse from text
parser = SyncLLMParser(client, "mistral-large-latest", structured_output=False)
When structured_output=False, the library only strips code fences and validates the result against the schema — it does not instruct the model how to respond. You are fully responsible for crafting a prompt that reliably produces output in the expected format. The output_format field is provided as a convenience variable you can reference in your template, but it has no effect unless you explicitly use it.
class SummaryPrompt(BaseTemplate):
"""
Summarize the following. Reply in {{ output_format }}.
{{ text }}
"""
text: str
output_format: str = "yaml"
Retry policy
Configure retries with exponential backoff and jitter via RetryPolicy:
from llmcast.parser.utils import RetryPolicy
policy = RetryPolicy(
n_tries=5,
backoff=1.0, # initial delay in seconds
multiplier=2.0, # exponential factor
max_backoff=30.0, # cap
jitter=True, # randomize to avoid thundering herd
)
parser = SyncLLMParser(client, "gpt-4o", retry_policy=policy)
Rate limit errors, timeouts, and server errors (429, 408, 5xx) are retried with backoff. Parse validation failures are also retried. Non-retryable errors (auth, bad request) are raised immediately.
A per-call policy can override the instance default:
result = parser.parse(prompt, MySchema, retry_policy=RetryPolicy(n_tries=1))
Token usage
Every .parse() call returns a (result, TokenUsage) tuple. If multiple attempts were needed, usage is summed across all of them.
result, usage = result
print(usage.prompt_tokens) # 98
print(usage.completion_tokens) # 44
print(usage.total_tokens) # 142
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file llmcast-0.1.0.tar.gz.
File metadata
- Download URL: llmcast-0.1.0.tar.gz
- Upload date:
- Size: 10.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffb78a6e80d08fe2bb5e2980be7438f92243086b4cfd269282d034398c095023
|
|
| MD5 |
16125168ad95154f86af779ec96ebf1b
|
|
| BLAKE2b-256 |
fdd2a7ec8a08bf466c2598fb5801a9ce0ba66a1ffd01924e43122779ca318e4d
|
File details
Details for the file llmcast-0.1.0-py3-none-any.whl.
File metadata
- Download URL: llmcast-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c590d068d8122a3451584873ec2881002defa5162e33bdd15beb5e7a9499120c
|
|
| MD5 |
d8956e9c52b3d947acfe107da8b49d48
|
|
| BLAKE2b-256 |
4869e737b6c620e72eed941e3389b44cda9988fb34359b6595d94c04df25cb7f
|