# https://compresr.ai/docs/framework-integration/langchain

> Human-readable page: https://compresr.ai/docs/framework-integration/langchain

The Compresr SDK ships first-party LangChain integrations under `compresr.integrations.langchain` (Python) and `@compresr/sdk/integrations/langchain` (TypeScript). They cover the four places LangChain users typically burn tokens: tool outputs in an agent loop, long chat history, the final outbound prompt, and retrieved documents in a `ContextualCompressionRetriever`. Every entry point calls Compresr under the hood: same auth, same `latte_v1`/`latte_v2` models, same parameter semantics as a direct SDK call.

> **Python is class-based, TypeScript is factory-based**
> In Python the three middlewares are **classes** (`CompresrToolMiddleware(...)`); in TypeScript they are **factory functions** (`compresrToolMiddleware({...})`). The document compressor `CompresrExtractor` is a class in both. Snake_case kwargs in Python; camelCase options in TypeScript.

## 1. Install

```bash
pip install compresr "langchain>=1.0"
export COMPRESR_API_KEY="cmp_..."
```

**TypeScript**

```bash
npm install @compresr/sdk langchain @langchain/core
export COMPRESR_API_KEY="cmp_..."
```

LangChain 1.0+ is required for the `create_agent` / `createAgent` middleware mechanism used by `CompresrToolMiddleware`, `CompresrSummarizationMiddleware`, and `CompresrPromptMiddleware`. The document compressor (`CompresrExtractor`) works on any version that ships `BaseDocumentCompressor`.

> **Extras are no-ops since 2.6.0**
> LangChain deps ship in the base install. `pip install compresr[langchain]` still resolves for back-compat but adds nothing.

## 2. Compress tool outputs in an agent

`CompresrToolMiddleware` plugs into `create_agent`. Each time a tool returns, the middleware compresses the result against the user's question before it lands in agent state, so the LLM's next reasoning step sees a shorter `ToolMessage`.

> **Middleware default is {{SDK_DEFAULT_MODEL}}**
> All three middlewares default to `compression_model="{{SDK_DEFAULT_MODEL}}"` (query-aware, requires a query). Pass `compression_model="{{DEFAULT_MODEL}}"` to opt into the newer backbone. `api_key=` can be omitted when `COMPRESR_API_KEY` is set in the environment or you have run `compresr-sdk login` (INI file at `~/.compresr/credentials`).

```python
import os
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from compresr.integrations.langchain import CompresrToolMiddleware

agent = create_agent(
    model=ChatOpenAI(model="gpt-4o-mini"),
    tools=[search_tool, fetch_url],
    middleware=[
        CompresrToolMiddleware(
            api_key=os.environ["COMPRESR_API_KEY"],
            target_compression_ratio=0.5,    # remove ~50% of tokens
            min_tokens=200,                   # skip tiny outputs
            allow_tools={"search_tool", "fetch_url"},
            query_arg="query",                # pull query out of tool args
        ),
    ],
)

result = agent.invoke({"messages": [{"role": "user", "content": "Why did the sale fall through?"}]})
```

**TypeScript**

```typescript
import { createAgent } from 'langchain';
import { ChatOpenAI } from '@langchain/openai';
import { compresrToolMiddleware } from '@compresr/sdk/integrations/langchain';

const agent = createAgent({
  model: new ChatOpenAI({ model: 'gpt-4o-mini' }),
  tools: [searchTool, fetchUrl],
  middleware: [
    compresrToolMiddleware({
      apiKey: process.env.COMPRESR_API_KEY!,
      targetCompressionRatio: 0.5,
      minTokens: 200,
      allowTools: ['search_tool', 'fetch_url'],
      queryArg: 'query',
    }),
  ],
});

const result = await agent.invoke({
  messages: [{ role: 'user', content: 'Why did the sale fall through?' }],
});
```

### CompresrToolMiddleware options

All keyword-only (Python) / options-object fields (TypeScript). Defaults shown. `allow_tools`, `ignore_tools`, and `query_arg` are specific to `CompresrToolMiddleware`; the summarization and prompt middlewares accept a different set (see below).

| Python | TypeScript | Default | Notes |
|---|---|---|---|
| `api_key` | `apiKey` | n/a | Required unless `client` is passed. |
| `client` | `client` | n/a | Pre-built `CompressionClient`: bypasses `api_key`/`base_url`. |
| `base_url` | `baseUrl` | `https://api.compresr.ai` | Override for self-hosted. |
| `compression_model` | `compressionModel` | `"{{SDK_DEFAULT_MODEL}}"` | The Compresr backbone routing the compression call. Defaults to `{{SDK_DEFAULT_MODEL}}`; pass `"{{DEFAULT_MODEL}}"` to opt into the newer backbone. |
| `target_compression_ratio` | `targetCompressionRatio` | `0.5` | `0 < r ≤ 1` removes that fraction; `r > 1` is Nx target. |
| `min_tokens` | `minTokens` | `200` | Skip tool outputs shorter than this. |
| `coarse` | `coarse` | server default | Paragraph-level vs token-level. |
| `allow_tools` | `allowTools` | `None` | Whitelist of tool names. Pass `allow_tools` OR `ignore_tools`, not both. |
| `ignore_tools` | `ignoreTools` | `None` | Blacklist. |
| `query` | `query` | n/a | Static query overriding everything else. |
| `query_extractor` | `queryExtractor` | n/a | `(tool_call, messages) -> str`, fully custom resolution. |
| `query_arg` | `queryArg` | n/a | Pull the query directly from a named tool arg. |
| `on_error` | `onError` | `"passthrough"` | `"raise"` to fail fast in tests. The type is exported as `ErrorPolicy` for typed configs. |

## 3. Replace old history with a compressed summary

`CompresrSummarizationMiddleware` is a KV-cache-friendly alternative to LangChain's `SummarizationMiddleware`. When the conversation crosses a token threshold, it compresses everything older than the last N messages into a single `[Earlier conversation summary] ...` `HumanMessage` and leaves recent messages untouched. **No LLM round-trip**: token-level compression.

```python
from compresr.integrations.langchain import CompresrSummarizationMiddleware

agent = create_agent(
    model=ChatOpenAI(model="gpt-4o-mini"),
    tools=tools,
    middleware=[
        CompresrSummarizationMiddleware(
            api_key=os.environ["COMPRESR_API_KEY"],
            max_tokens_before_summary=4_000,   # trigger
            messages_to_keep=20,                # recent messages preserved verbatim
            target_compression_ratio=4,         # 4× compression of the older block
        ),
    ],
)
```

**TypeScript**

```typescript
import { compresrSummarizationMiddleware } from '@compresr/sdk/integrations/langchain';

const agent = createAgent({
  model: new ChatOpenAI({ model: 'gpt-4o-mini' }),
  tools,
  middleware: [
    compresrSummarizationMiddleware({
      apiKey: process.env.COMPRESR_API_KEY!,
      maxTokensBeforeSummary: 4_000,
      messagesToKeep: 20,
      targetCompressionRatio: 4,
    }),
  ],
});
```

The middleware detects its own prior summary via the `[Earlier conversation summary]` prefix and refuses to re-summarize an already-summarized prefix, so it's safe to run on every turn.

Aliases `trigger` / `keep` are accepted in place of `max_tokens_before_summary` / `messages_to_keep` to match LangChain's own `SummarizationMiddleware` naming.

### CompresrSummarizationMiddleware options

| Python | TypeScript | Default | Notes |
|---|---|---|---|
| `max_tokens_before_summary` (alias `trigger`) | `maxTokensBeforeSummary` (alias `trigger`) | `4000` | Token threshold that triggers a summary. |
| `messages_to_keep` (alias `keep`) | `messagesToKeep` (alias `keep`) | `20` | Recent messages preserved verbatim. |
| `target_compression_ratio` | `targetCompressionRatio` | `0.5` | Applied to the older-messages block. |
| `token_counter` | `tokenCounter` | SDK `estimate_tokens` | char/4 heuristic, upgrades to tiktoken `cl100k_base` when available. |
| `query` | `query` | n/a | Static query. |
| `query_extractor` | `queryExtractor` | n/a | `(messages) -> str`, custom resolution. |
| `client` | `client` | n/a | Pre-built `CompressionClient`. |
| `api_key` | `apiKey` | n/a | Required unless `client` is passed. |
| `base_url` | `baseUrl` | `https://api.compresr.ai` | Self-hosted override. |
| `compression_model` | `compressionModel` | `"{{SDK_DEFAULT_MODEL}}"` | |
| `coarse` | `coarse` | server default | Paragraph vs token level. |
| `on_error` | `onError` | `"passthrough"` | Exported as `ErrorPolicy`. |

## 4. Cap the outbound prompt with `CompresrPromptMiddleware`

The last-mile budget cap. `CompresrPromptMiddleware` runs in `wrap_model_call` and walks the messages largest-first, compressing just enough to fit under `max_tokens`. It mutates the model request, **not** agent state; the next turn still sees the original messages.

```python
from compresr.integrations.langchain import CompresrPromptMiddleware

agent = create_agent(
    model=ChatOpenAI(model="gpt-4o-mini"),
    tools=tools,
    middleware=[
        CompresrPromptMiddleware(
            api_key=os.environ["COMPRESR_API_KEY"],
            max_tokens=8_000,        # hard ceiling, REQUIRED
            min_tokens=500,           # override default 200 to skip more short messages
            target_compression_ratio=0.5,
        ),
    ],
)
```

**TypeScript**

```typescript
import { compresrPromptMiddleware } from '@compresr/sdk/integrations/langchain';

const agent = createAgent({
  model: new ChatOpenAI({ model: 'gpt-4o-mini' }),
  tools,
  middleware: [
    compresrPromptMiddleware({
      apiKey: process.env.COMPRESR_API_KEY!,
      maxTokens: 8_000,    // hard ceiling, REQUIRED
      minTokens: 500,      // override default 200 to skip more short messages
      targetCompressionRatio: 0.5,
    }),
  ],
});
```

### CompresrPromptMiddleware options

| Python | TypeScript | Default | Notes |
|---|---|---|---|
| `max_tokens` | `maxTokens` | n/a | **Required.** Hard ceiling for the outbound prompt. |
| `min_tokens` | `minTokens` | `200` | Don't touch messages smaller than this. |
| `target_compression_ratio` | `targetCompressionRatio` | `0.5` | Per-message removal strength. |
| `token_counter` | `tokenCounter` | SDK `estimate_tokens` | Same char/4 heuristic with tiktoken upgrade. |
| `coarse` | `coarse` | server default | Paragraph vs token level. |
| `query` | `query` | n/a | Static query. |
| `query_extractor` | `queryExtractor` | n/a | `(messages) -> str`, custom resolution. |
| `client` | `client` | n/a | Pre-built `CompressionClient`. |
| `api_key` | `apiKey` | n/a | Required unless `client` is passed. |
| `base_url` | `baseUrl` | `https://api.compresr.ai` | Self-hosted override. |
| `compression_model` | `compressionModel` | `"{{SDK_DEFAULT_MODEL}}"` | |
| `on_error` | `onError` | `"passthrough"` | Exported as `ErrorPolicy`. |

Compose all three middlewares in the same `create_agent`/`createAgent` call. They cover orthogonal token sources:

```python
middleware=[
    CompresrToolMiddleware(api_key=KEY, query_arg="query"),            # per-tool
    CompresrSummarizationMiddleware(api_key=KEY, max_tokens_before_summary=4_000),
    CompresrPromptMiddleware(api_key=KEY, max_tokens=8_000),           # last-mile cap
]
```

## 5. Compress retrieved documents

`CompresrExtractor` is a `BaseDocumentCompressor`: a drop-in replacement for `LLMChainExtractor` inside a `ContextualCompressionRetriever`. Batches all eligible documents into a single Compresr call (up to 100 per batch).

```python
from langchain.retrievers import ContextualCompressionRetriever
from compresr.integrations.langchain import CompresrExtractor

extractor = CompresrExtractor(
    api_key=os.environ["COMPRESR_API_KEY"],
    target_compression_ratio=0.6,
    min_tokens=120,
    drop_below_min=False,    # set True to filter out docs that come back empty
)

compressing_retriever = ContextualCompressionRetriever(
    base_compressor=extractor,
    base_retriever=vectorstore.as_retriever(search_kwargs={"k": 20}),
)

docs = compressing_retriever.invoke("What changed in the Q3 forecast?")
```

**TypeScript**

```typescript
import { ContextualCompressionRetriever } from 'langchain/retrievers/contextual_compression';
import { CompresrExtractor } from '@compresr/sdk/integrations/langchain';

const extractor = new CompresrExtractor({
  apiKey: process.env.COMPRESR_API_KEY!,
  targetCompressionRatio: 0.6,
  minTokens: 120,
  dropBelowMin: false,
});

const compressingRetriever = new ContextualCompressionRetriever({
  baseCompressor: extractor,
  baseRetriever: vectorstore.asRetriever({ k: 20 }),
});

const docs = await compressingRetriever.invoke("What changed in the Q3 forecast?");
```

The extractor sets `metadata["compresr"] = True` on every document it touched and leaves documents below `min_tokens` unchanged (or filters them out if `drop_below_min=True`).

## 6. Wrap a single tool

If you only need compression on one tool, without an agent middleware, wrap it directly. `wrap_tool_with_compression` / `wrapToolWithCompression` returns a new `StructuredTool` preserving `name`, `description`, `args_schema`, `return_direct`, and error handlers.

```python
from langchain_core.tools import tool
from compresr.integrations.langchain import wrap_tool_with_compression

@tool
def fetch_article(url: str) -> str:
    """Fetch the article body at the given URL."""
    ...

compressed_fetch = wrap_tool_with_compression(
    fetch_article,
    api_key=os.environ["COMPRESR_API_KEY"],
    target_compression_ratio=4,
    query_arg="url",    # or query_extractor=lambda args: args["topic"]
    min_tokens=200,
)
```

**TypeScript**

```typescript
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { wrapToolWithCompression } from '@compresr/sdk/integrations/langchain';

const fetchArticle = tool(
  async ({ url }) => fetchArticleBody(url),
  {
    name: 'fetch_article',
    description: 'Fetch the article body at the given URL.',
    schema: z.object({ url: z.string() }),
  },
);

const compressedFetch = wrapToolWithCompression(fetchArticle, {
  apiKey: process.env.COMPRESR_API_KEY!,
  targetCompressionRatio: 4,
  queryArg: 'url',
  minTokens: 200,
});
```

Python raises `TypeError` if the input isn't a `StructuredTool`; wrap a raw function with `@tool` first. TypeScript is more permissive: `wrapToolWithCompression` accepts anything exposing `name` plus `func`, `_call`, or `invoke`.

For the case where you own the tool's source, there is also a decorator form, `compress_tool_output` / `compressToolOutput`, that stacks on top of `@tool`:

```python
from compresr.integrations.langchain import compress_tool_output

@compress_tool_output(api_key=os.environ["COMPRESR_API_KEY"], query_arg="url", target_compression_ratio=4)
@tool
def fetch_article(url: str) -> str:
    """Fetch the article body at the given URL."""
    ...
```

## When this helps

- **Agent loops calling verbose tools**: search, scrape, RAG fetch. `CompresrToolMiddleware` compresses each tool result once, against the user's question, before the LLM ever sees it.
- **Long-running chat agents**: `CompresrSummarizationMiddleware` keeps the prompt bounded without forcing a slow LLM summary call.
- **Bounded model spend per call**: `CompresrPromptMiddleware` enforces an absolute outbound budget regardless of which middleware fired earlier.
- **High-recall retrieval**: `top_k=20+` plus `CompresrExtractor` keeps the retrieved payload tight without dropping documents entirely.

## Related

- [LangGraph](/docs/framework-integration/langgraph): same middlewares applied inside `StateGraph`, plus node-level helpers, lossy checkpoint serializer, lossy store wrapper, and multi-agent handoff.
- [Models](/docs/api-reference/models): `latte_v2` parameter semantics (`target_compression_ratio`, `coarse`, and friends).
- [RAG guide](/docs/guides/rag): the underlying retrieve → compress → answer pipeline.
