Skip to main content

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 system_prompt, tools, inference_params, and response_format once with LLM.bind(). Use the resulting BoundLLM with generate_one(), generate_many(), or stream_one().
  • Types that follow your configuration. Passing response_format=Answer makes response.output an Answer. Passing tools also determines the tool_manager type and whether generate_one() can return ToolCallTurn.
  • Easy-to-discover result variants. Every GenerateResult, DispatchOutcome, and DispatchManyOutcome variant has a literal .kind value. Every StreamItem variant except str does 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, and input_tokens_cache_read.
  • Retries that cooperate. One SharedBackoff coordinates concurrency, request-start pacing, and provider-directed pauses across models that share a rate-limit quota.
  • Complete billing. Successful results and GenerationError values 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.19.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langchaint 0.19.0
File Size Uploaded
langchaint-0.19.0.tar.gz 162.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for langchaint 0.19.0
File Interpreter ABI Platform
langchaint-0.19.0-py3-none-any.whl Python 3 none any Details

Total release size: 341.5 kB

Release files / langchaint-0.19.0.tar.gz

Download URL langchaint-0.19.0.tar.gz
Size 162.4 kB
Tags Source
SHA-256 checksum
How to use checksums
ac5a7ac0cfc98bc8f7dc6b130cb9b45b6ae45e928acc908a64280cd360d8b10c
BLAKE2b-256 checksum
How to use checksums
fa979f34fc6d8a8add43b17358ea4e8f9096ee012000444f5f35b536bcfe4c50
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","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.19.0-py3-none-any.whl

Download URL langchaint-0.19.0-py3-none-any.whl
Size 179.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f806b610855d9400b5497325dbb8f0274670f8598543e8e5af1ac05205e00b7f
BLAKE2b-256 checksum
How to use checksums
7f411d5cfbb5bc41947f6556f8318994950d5a5b3db658cbf456303d5f428b9d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","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 history Release notifications | RSS feed

0.23.3

2 release files

0.23.2

2 release files

0.23.1

2 release files

0.23.0

2 release files

0.22.0

2 release files

0.21.2

2 release files

0.19.4

2 release files

0.19.3

2 release files

0.19.1

2 release files

This release

0.19.0 This release

2 release files

0.18.1

2 release files

0.18.0

2 release files

0.17.1

2 release files

0.17.0

2 release files

0.16.0

2 release files

0.15.3

2 release files

0.15.2

2 release files

0.15.1

2 release files

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.11.2

2 release files

0.11.1

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page