---
title: Compression
summary: compress() and the report it returns.
canonical: https://docs.caveman.so/docs/sdk/compression
license: MIT
capability: sdk-ts
updated: 2026-09-16T21:32:40-07:00
basis: inferred
---

# Compression

> compress() and the report it returns.
`cave.compress()` sends one payload to the compression endpoint on your configured service and returns the report it gets back: the smaller text, the token counts before and after, the method that produced it, and a handle for the stored original. Every figure in the report comes from the engine.

Want tool results compressed inside a framework you already use, with no call site to change? Use [middleware](/docs/sdk/middleware).

### TypeScript

```ts
import { Cave } from "@caveman-ai/sdk";

const { CAVE_API_KEY = "", CAVE_BASE_URL = "" } = process.env;
const cave = new Cave({ apiKey: CAVE_API_KEY, baseURL: CAVE_BASE_URL, agent: "support-agent" });

const payload = JSON.stringify({ rows: [{ id: 1 }, { id: 2 }] });
const report = await cave.compress(payload, { contentType: "json" });

console.log(report.output, report.tokensBefore, report.tokensAfter, report.ratio, report.recoveryHandle);
```

### Python

```python
import json, os
from caveman_cloud import Cave

cave = Cave(api_key=os.environ["CAVE_API_KEY"], base_url=os.environ["CAVE_BASE_URL"], agent="support-agent")

payload = json.dumps({"rows": [{"id": 1}, {"id": 2}]})
report = cave.compress(payload, content_type="json")

print(report.output, report.tokens_before, report.tokens_after, report.ratio, report.recovery_handle)
```

The signatures differ by one thing. TypeScript takes an options object, `compress(payload, options?: CompressOptions)`. Python takes a keyword-only argument, `compress(payload, *, content_type=None)`.

## The report

| TypeScript | Python | What it holds |
| --- | --- | --- |
| `output` | `output` | The compressed payload, or the original input verbatim on pass-through |
| `contentType` | `content_type` | The type the engine detected or you declared |
| `tokensBefore` | `tokens_before` | Estimated tokens for the input, `0` when nothing was counted |
| `tokensAfter` | `tokens_after` | Estimated tokens for the output, `0` when nothing was counted |
| `ratio` | `ratio` | Fraction of tokens removed, `(before - after) / before`, `0` when unchanged |
| `basis` | `basis` | Always `"inferred"` |
| `tokenCountBasis` | `token_count_basis` | The counter the engine used, such as `o200k_base` or `approx_chars_div_4` |
| `recoveryHandle` | `recovery_handle` | Handle for the byte-exact original, absent when nothing was stored |
| `method` | `method` | The compressor the engine chose, such as `toon` or `elision` |
| `losslessToModel` | `lossless_to_model` | `true` when the model-visible output kept the full value |

`ratio` is recomputed by the SDK from `tokensBefore` and `tokensAfter` rather than copied from the response, so it always matches the two counts beside it. `basis` is `"inferred"` on every result the client library returns: a local estimate for one payload, earned offline. [Numbers and limits](/docs/counting) defines the three words.

## Pass-through

Any transport or parse problem returns the original bytes with a ratio of `0`. That covers a connection failure, a non-200 status, a malformed body, a missing `output` field, counts outside the non-negative integers, and an `after` count larger than the `before` count.

This is what a pass-through looks like, captured by pointing the client at a closed port and compressing `{"rows":[{"id":1},{"id":2}]}`:

```json
{
  "output": "{\"rows\":[{\"id\":1},{\"id\":2}]}",
  "contentType": "json",
  "tokensBefore": 0,
  "tokensAfter": 0,
  "ratio": 0,
  "basis": "inferred",
  "tokenCountBasis": "unavailable"
}
```

Python returns the same values in a `CompressResult` dataclass, with `recovery_handle=None`, `method=None` and `lossless_to_model=None` present and empty.

Two fields tell a pass-through from a real result: `ratio` is `0` and `tokenCountBasis` is `"unavailable"`. A successful call always reports the counter the engine used.

## Content types

`contentType` is a hint for the engine's detector, and the engine detects the type itself when you leave it out. The documented values are `json`, `toon`, `log`, `code`, `diff`, `search-result`, `text` and `toolschema`. The type comes back on the report, so you can check what the engine decided.

`toon` is the one value that forces a choice rather than hinting at one: it selects the token-oriented JSON encoding described on [TOON](/docs/proxy/compressors/toon). [All compressors](/docs/proxy/compressors) covers what each type does to a payload.

## Recovering the original

When the engine stored the original, the report carries `recoveryHandle`. The handle names the stored bytes rather than carrying them.

The client library has one job with that handle: give it to you. Retrieval belongs to the surface that stored the original, which is the engine behind your service. [Recovery](/docs/proxy/recoverable) covers how a handle is turned back into the original and when compression declines to run at all. When a model needs to pull the original back mid-conversation, that is what the middleware runtime's recovery tool is for, on [Middleware](/docs/sdk/middleware).

**A saving here is one payload, measured offline**
`tokensBefore` and `tokensAfter` are the engine's own count of one string, with the counter named in `tokenCountBasis`. They describe that one string offline, and the SDK never multiplies them into a period or a currency.

## Limits

Small payloads often come back unchanged with a real `tokenCountBasis` and a `ratio` of `0`. That is a genuine result: the engine looked and kept the payload as it was.

`compress()` takes one payload per call and holds it in memory in both languages.
