langchaint
langchaint is an opinionated, provider-neutral Python client for LLM applications. It provides fully typed, asynchronous APIs for generation, streaming, embeddings, tools, retries, and billing. The application owns the agent loop.
Alpha: the API is unstable and may change without notice.
Why langchaint
- One consistent API. Set request fields once with
LLM.bind(). Use the resultingBoundLLMwithgenerate_one(),generate_many(),generate_many_records(), orstream_one(). - Types that follow your configuration. Passing
response_format=Answermakesresponse.outputanAnswer. Passingtoolsalso determines thetool_managertype and whethergenerate_one()can returnToolCallTurn. - Easy-to-discover result variants. Every
GenerateResult,DispatchOutcome, andDispatchManyOutcomevariant has a literal.kindvalue. EveryStreamItemvariant exceptstrdoes too. Editors can autocomplete the values, so a match statement needs no class imports. - Names that explain themselves. Public names state their meaning, units, and scope through names such as
timeout_seconds,max_attempts,cost_in_usd, andinput_tokens_cache_read. - Retries that cooperate. One
SharedBackoffcoordinates concurrency, request-start pacing, and provider-directed pauses across models that share a rate-limit quota. - Complete billing. Successful results and
GenerationErrorvalues retain provider-reported usage from every recorded attempt, including billed retries. - Streaming with clear ownership.
stream_one()returns an async context manager and async iterator.final()returns the typed result with its usage and attempt history. - Agent loops in plain Python. Provider-neutral messages, typed tools, concurrent dispatch, and explicit result variants support ordinary async control flow.
langchaint ships py.typed.
Static type information preserves structured output types through generation, batching, and streaming.
Install
langchaint requires Python 3.13 or newer.
Install the extra for each backend you use:
pip install "langchaint[openai]"
| Backend | Class | Install |
|---|---|---|
| Anthropic | Anthropic |
langchaint[anthropic] |
| Anthropic on Amazon Bedrock | AnthropicBedrock |
langchaint[anthropic-bedrock] |
| Cohere embeddings on Amazon Bedrock | CohereBedrock |
langchaint[cohere-bedrock] |
| DeepSeek | DeepSeek |
langchaint[deepseek] |
| Gemini | Gemini |
langchaint[gemini] |
| OpenAI | OpenAI |
langchaint[openai] |
| OpenAI embeddings | OpenAI |
langchaint[openai-embedding] |
| OpenAI on Amazon Bedrock | OpenAIBedrock |
langchaint[openai-bedrock] |
Install langchaint[tracing] for OTel tracing.
Generate a typed response
import asyncio
from pydantic import BaseModel
from langchaint.openai import OpenAI
class Answer(BaseModel):
answer: str
confidence: float
async def main() -> None:
assistant = (
OpenAI()
.model("gpt-5.6-terra")
.bind(
system_prompt="Answer clearly and concisely.",
response_format=Answer,
)
)
response = await assistant.generate_one("Why is the sky blue?")
print(response.output.answer)
print(response.usage.cost_in_usd)
asyncio.run(main())
The Pydantic model determines the output type and validates the provider response. The response retains the complete assistant turn, raw SDK response, stop reason, per-attempt history, and billing.
generate_many() returns one result per input in input order.
A terminal failure becomes that input's GenerationError, so sibling results remain available.
Coordinate retries across a rate-limit quota
Create one OpenAI for each rate-limit quota:
openai = OpenAI(
max_concurrent_requests=8,
max_request_starts_per_second=50.0,
)
fast_model = openai.model("gpt-5.6-luna")
strong_model = openai.model("gpt-5.6-sol")
Both LLM values share one SharedBackoff.
A rate-limit response pauses request starts across the shared quota.
A transient failure local to one request waits and retries that request.
Each binding keeps its own max_attempts limit.
Unlike independent retry loops, SharedBackoff can slow the whole quota as soon as one request receives a rate-limit response.
Stream with an explicit lifetime
text_assistant = OpenAI().model("gpt-5.6-terra").bind()
async with text_assistant.stream_one("Explain photosynthesis.") as stream:
async for item in stream:
if isinstance(item, str):
print(item, end="", flush=True)
response = await stream.final()
The context manager owns the provider stream and the shared request permit.
Iteration exposes text, reasoning, and tool-call StreamItem values.
final() drains unread StreamItem values and returns the assembled Response or ToolCallTurn.
Build agent loops with ordinary async Python
langchaint provides the messages, typed tools, argument validation, concurrent dispatch, and result variants needed for an agent loop. The application controls turn limits, state, approvals, model changes, and persistence.
messages: list[Message] = [UserMessage(content=prompt)]
for _ in range(max_turns):
result = await bound.generate_one(messages)
match result.kind:
case "tool_call_turn":
messages.append(result.assistant_message)
outcomes = await bound.tool_manager.dispatch_many(result.tool_calls)
messages.extend(outcome.tool_message for outcome in outcomes)
case "response":
return result.output
raise RuntimeError("model did not finish within max_turns")
ToolManager.dispatch_many() runs parallel tool calls concurrently and preserves their order.
See examples/02_tool_loop.py for a complete typed tool loop.
Account for the complete call
response.usage.cost_in_usd includes every billed retry recorded for the call.
response.usage_successful_attempt.cost_in_usd reports the kept answer.
GenerationError.usage preserves the recorded cost of failed calls.
Usage separates uncached input, cache reads, cache writes, output, reasoning output, and provider-executed tool charges.
Each Billing records the service tier and token rates applied to one attempt.
When a used category has no known rate, its cost is NaN instead of zero.
More examples
examples/README.md covers structured output, batches, streaming, tools, tracing, pricing, failures, prompt caching, reasoning, embeddings, and application structure.
License
langchaint uses the MIT License.
Release files for langchaint 0.20.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| langchaint-0.20.1.tar.gz | 176.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| langchaint-0.20.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 371.3 kB
Release files / langchaint-0.20.1.tar.gz
| Download URL | langchaint-0.20.1.tar.gz |
|---|---|
| Size | 176.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e0a0b2306e252e07bcb6c9cac6c1207e6281042bad7e88b8cd93ab5e8cb97363
|
|
BLAKE2b-256 checksum How to use checksums |
2a059fd976374ef8115faa7d71316f151c94bc2104970e92a57d387ced4adbe9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / langchaint-0.20.1-py3-none-any.whl
| Download URL | langchaint-0.20.1-py3-none-any.whl |
|---|---|
| Size | 194.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
514b0cc8fa089b412ac9078e4d85270a7431aa90674a1f7a1df326d1d9be990a
|
|
BLAKE2b-256 checksum How to use checksums |
57d1a90f1ecf146984dc13e0dc62255240859ae23290a50e6201ea67e42ee0a2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|