Skip to main content

A context-aware chat harness primitive built on LangChain and LangGraph.

Project description

autochat

autochat is a small Python library for building context-aware chat applications on top of LangGraph and LangChain.

It gives your app a clean chat harness primitive:

from autochat import AutoChat

chat = AutoChat(...)

Then you can invoke or stream the graph while passing your own runtime context into tools, retrievers, processors, and graph execution.

autochat is still under construction. It is not published to PyPI yet, but it will soon be installable as autochat with pip, uv, and other standard Python package managers.

Why

Most production chat apps need the same foundation:

  • model and tool orchestration
  • runtime context for auth, tenancy, request metadata, and app services
  • context-aware tools and retrievers
  • thread persistence
  • optional history compression
  • a simple async invoke/stream API

autochat packages those pieces into a small, typed, async-first interface.

Installation

For local development:

uv sync

For examples that use OpenAI models:

uv sync --dev

Future install flow:

pip install autochat

or:

uv add autochat

Quick Start

import asyncio
from dataclasses import dataclass

from langchain_openai import ChatOpenAI

from autochat import AutoChat, ChatConfig, ChatRuntime, chat_tool


@dataclass(frozen=True)
class AppContext:
    user_id: str
    plan: str


@chat_tool(name="current_plan")
async def current_plan(runtime: ChatRuntime[AppContext]) -> str:
    return f"The user is on the {runtime.context.plan} plan."


async def main() -> None:
    chat = AutoChat[AppContext](
        config=ChatConfig(model=ChatOpenAI(model="gpt-5-nano")),
        tools=[current_plan],
        system_message="You are concise and practical.",
    )

    result = await chat.ainvoke(
        "What plan am I on?",
        thread_id="thread_123",
        context=AppContext(user_id="user_1", plan="pro"),
    )

    print(result["messages"][-1].content)


if __name__ == "__main__":
    asyncio.run(main())

Run the included examples:

uv run python examples/basic_tool_example.py
OPENAI_API_KEY=... uv run --dev python examples/basic_chat_example.py
OPENAI_API_KEY=... uv run --dev python examples/basic_retriever_example.py
OPENAI_API_KEY=... uv run --dev python examples/basic_persistence_compression_example.py

Runtime Context

ChatRuntime[TContext] is created for each chat run and passed through the graph layer.

Use it to carry app-specific data like user IDs, org IDs, permissions, request metadata, database handles, or tenant config.

@dataclass(frozen=True)
class AppContext:
    org_id: str
    permissions: set[str]


@chat_tool(name="billing_status")
async def billing_status(runtime: ChatRuntime[AppContext]) -> str:
    return f"Billing is active for {runtime.context.org_id}."

Tools

Use @chat_tool for native AutoChat tools. Function schemas are inferred from normal function parameters, and runtime is injected automatically.

@chat_tool(name="calculator")
async def calculator(
    a: float,
    b: float,
    runtime: ChatRuntime[AppContext],
) -> float:
    return a + b

You can also wrap LangChain tools:

from autochat import ChatTool

chat = AutoChat(
    config=ChatConfig(model=model),
    tools=[ChatTool(langchain_tool)],
)

Tool Processors

Preprocessors and postprocessors wrap tool execution with app logic such as auth checks, input normalization, logging, or cleanup.

from autochat import ToolInvocation


def require(permission: str):
    def processor(invocation: ToolInvocation[AppContext, object]) -> object:
        if permission not in invocation.runtime.context.permissions:
            raise PermissionError(f"Missing permission: {permission}")
        return invocation.input

    return processor


@chat_tool(name="billing_status", preprocessors=[require("billing.read")])
async def billing_status(runtime: ChatRuntime[AppContext]) -> str:
    return "Billing is active."

Retrieval

Retrievers are exposed to the model as callable retrieval tools. The model decides when to call them, and AutoChat executes the retriever with the current ChatRuntime.

from autochat import ChatRetriever, ChatRuntime


async def search_docs(query: str, runtime: ChatRuntime[AppContext]) -> list[str]:
    return [f"Docs for {runtime.context.org_id}: {query}"]


chat = AutoChat[AppContext](
    config=ChatConfig(model=model),
    retrievers=[
        ChatRetriever(
            search_docs,
            name="docs",
            description="Search organization documentation.",
        )
    ],
    system_message="Use the docs retriever for policy or product questions.",
)

Persistence

AutoChat uses LangGraph checkpointers for thread persistence. Pass a checkpointer with persistence=..., and LangGraph stores graph state by thread_id.

from langgraph.checkpoint.memory import InMemorySaver

chat = AutoChat(
    config=ChatConfig(model=model),
    persistence=InMemorySaver(),
)

For production, swap InMemorySaver for a durable LangGraph saver.

Compression

Compression is optional. It runs before the model call, after persisted thread state has been loaded.

from autochat import AutoCompress, SummarizeAll
from langgraph.checkpoint.memory import InMemorySaver

chat = AutoChat(
    config=ChatConfig(
        model=model,
        context_window=128_000,
    ),
    persistence=InMemorySaver(),
    compression=AutoCompress(
        at=0.6,
        strategy=SummarizeAll(),
    ),
)

Available strategies:

  • SummarizeAll(): summarize older history into one summary message
  • SummarizeLatestN(n=20): summarize only the latest n historical messages
  • KeepLatestN(n=20): keep only the latest n messages without summarizing

Summaries replace graph history using LangGraph message removal, so future turns see a compacted thread state.

Core Pieces

  • AutoChat: public chat harness for invoke and stream workflows
  • ChatConfig: model configuration and context-window metadata
  • ChatRuntime[TContext]: per-run context passed through graph execution
  • ChatTool / @chat_tool: LangChain-compatible and native context-aware tools
  • ChatRetriever: LangChain-compatible and native context-aware retrievers
  • AutoCompress: optional automatic thread compression
  • ChatGuideline: lightweight reusable instruction primitive

Project Structure

A high-level map for contributors:

src/autochat/
  chat.py              AutoChat public harness API
  config.py            ChatConfig and model configuration
  guidelines.py        Lightweight guideline primitives
  runtime/             Invocation-scoped runtime context
  tools/               ChatTool, @chat_tool, processor types
  retrieval/           ChatRetriever, retrieval config, RAG strategies
  compression/         AutoCompress and compression strategies
  graph/               LangGraph state, builder, runtime wiring, execution
  exceptions/          Library exception types

examples/
  basic_tool_example.py                    Context-aware tool + processor
  basic_chat_example.py                    AutoChat + model + tools
  basic_retriever_example.py               AutoChat + retriever
  basic_persistence_compression_example.py Persistence + compression

The intended dependency direction is:

AutoChat
  -> graph
      -> tools / retrieval / compression
          -> runtime

Contributing

The library is early and the API is still being shaped. Contributions should keep the surface area small, typed, and pleasant for application developers.

Before changing internals, run the examples when relevant:

uv run python examples/basic_tool_example.py
uv run --dev python examples/basic_chat_example.py
uv run --dev python examples/basic_retriever_example.py
uv run --dev python examples/basic_persistence_compression_example.py

Design preferences:

  • async-first internally
  • explicit runtime context
  • native primitives with LangChain compatibility
  • LangGraph persistence instead of custom thread storage
  • minimal graph details in user-facing APIs

License

autochat is released under the MIT License. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

autochatlib-0.1.0.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

autochatlib-0.1.0-py3-none-any.whl (24.6 kB view details)

Uploaded Python 3

File details

Details for the file autochatlib-0.1.0.tar.gz.

File metadata

  • Download URL: autochatlib-0.1.0.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for autochatlib-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8a830a14da1523662d84e8e200b8b4bf37ef6762315299b5b9e2fa5540051881
MD5 5c2d2010c8c767d3f7fdbb11a37f8494
BLAKE2b-256 9fbc79a0663fcc68a986daf14c16ee58b76abbfac440bcd63960463b961cf72f

See more details on using hashes here.

File details

Details for the file autochatlib-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: autochatlib-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 24.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for autochatlib-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f3d598544e6313719b4a4d7328c23d45e094667a69a32d7ec5d66772fef9c455
MD5 d0d67f2176831236fb04c252b89ba96a
BLAKE2b-256 5f7ba50c888b98ca7ab19cda7b4e08eaaf8d9d2e397773422e499e92caf7660a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page