# https://compresr.ai/docs/framework-integration/llm-providers

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

Compresr doesn't call the provider for you. It returns a shorter string that you drop into the call you already make, in the slot the provider treats as reference material (`system`, `system_instruction`, or top-level `system`). Pick your provider:

## Client setup

Every recipe below assumes a single `client` built once at module scope. Same shape as the [Python SDK reference](/docs/sdks/python#2-client-construction).

```python
import os
from compresr import CompressionClient

client = CompressionClient(api_key=os.environ["COMPRESR_API_KEY"])
```

**TypeScript**

```typescript
import { CompressionClient } from '@compresr/sdk';

const client = new CompressionClient({
  apiKey: process.env.COMPRESR_API_KEY!,
});
```

**cURL**

```bash
# No client object - export the key once.
export COMPRESR_API_KEY="cmp_your_api_key"
```

> `CompressResponse.data` is `Optional[CompressResult]` in Python and nullable in TS; on error it is `None`/`null`. The recipes below read `.data.compressed_context` directly and assume the call succeeded. In production, wrap the compress call in `try/except CompresrError` (Py) or check `if (!result.data) throw new Error(result.error ?? 'compress failed')` (TS) before piping the string to the provider. See [errors](/docs/api-reference/errors).

The OpenAI chat completions API treats the first `{"role": "system", ...}` message as instructions. Drop the compressed text there; keep the user's actual question in a `user` message.

```python
from openai import OpenAI

openai_client = OpenAI()

compressed = client.compress(
    context=long_document,
    query=user_question,
    compression_model_name="{{DEFAULT_MODEL}}",
).data.compressed_context

response = openai_client.chat.completions.create(
    model="gpt-5",
    messages=[
        {"role": "system", "content": compressed},
        {"role": "user", "content": user_question},
    ],
)
print(response.choices[0].message.content)
```

**TypeScript**

```typescript
import OpenAI from 'openai';

const openai = new OpenAI();

const { data } = await client.compress({
  context: longDocument,
  query: userQuestion,
  compressionModelName: '{{DEFAULT_MODEL}}',
});

const response = await openai.chat.completions.create({
  model: 'gpt-5',
  messages: [
    { role: 'system', content: data.compressed_context },
    { role: 'user', content: userQuestion },
  ],
});
console.log(response.choices[0].message.content);
```

**cURL**

```bash
# 1. Compress.
COMPRESSED=$(curl -s -X POST https://api.compresr.ai/api/compress/question-specific/ \
  -H "X-API-Key: $COMPRESR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "context": "<long document>",
    "query": "<user question>",
    "compression_model_name": "{{DEFAULT_MODEL}}"
  }' | jq -r '.data.compressed_context')

# 2. Pass to OpenAI.
curl -X POST https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"model\": \"gpt-5\",
    \"messages\": [
      {\"role\": \"system\", \"content\": $(jq -Rs . <<< \"$COMPRESSED\")},
      {\"role\": \"user\", \"content\": \"<user question>\"}
    ]
  }"
```

Compresr's token counts use `tiktoken.encoding_for_model(compression_model_name)` when the model is known, falling back to `cl100k_base` otherwise. That resolves to `cl100k_base` for the GPT-4 family and `o200k_base` for the GPT-5 family; count deltas from OpenAI's billing are usually within 1%. Fewer input tokens also means a faster prefill, so first-token latency drops noticeably on chat UIs.

Anthropic's Messages API takes the system prompt as a top-level `system` field, not as a message in the array. Drop the compressed string into that field; the user's question goes into a single `user` message.

```python
from anthropic import Anthropic

anthropic = Anthropic()

compressed = client.compress(
    context=long_document,
    query=user_question,
    compression_model_name="{{DEFAULT_MODEL}}",
).data.compressed_context

response = anthropic.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": compressed,
            "cache_control": {"type": "ephemeral"},
        }
    ],
    messages=[{"role": "user", "content": user_question}],
)
print(response.content[0].text)
```

**TypeScript**

```typescript
import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic();

const { data } = await client.compress({
  context: longDocument,
  query: userQuestion,
  compressionModelName: '{{DEFAULT_MODEL}}',
});

const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-5',
  max_tokens: 1024,
  system: [
    {
      type: 'text',
      text: data.compressed_context,
      cache_control: { type: 'ephemeral' },
    },
  ],
  messages: [{ role: 'user', content: userQuestion }],
});
console.log((response.content[0] as { text: string }).text);
```

**cURL**

```bash
# 1. Compress.
COMPRESSED=$(curl -s -X POST https://api.compresr.ai/api/compress/question-specific/ \
  -H "X-API-Key: $COMPRESR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "context": "<long document>",
    "query": "<user question>",
    "compression_model_name": "{{DEFAULT_MODEL}}"
  }' | jq -r '.data.compressed_context')

# 2. Pass to Anthropic.
curl -X POST https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d "{
    \"model\": \"claude-sonnet-4-5\",
    \"max_tokens\": 1024,
    \"system\": [
      {
        \"type\": \"text\",
        \"text\": $(jq -Rs . <<< \"$COMPRESSED\"),
        \"cache_control\": {\"type\": \"ephemeral\"}
      }
    ],
    \"messages\": [{\"role\": \"user\", \"content\": \"<user question>\"}]
  }"
```

Claude's tokenizer counts slightly differently from tiktoken, so the savings ratio carries over but the exact billed-token number will diverge from the Compresr response. Anthropic caches only blocks tagged with `cache_control: {"type": "ephemeral"}` — the snippets above set that marker on the compressed `system` block, so reusing the same string across turns lands cache hits on subsequent calls.

Gemini's `generate_content` API takes reference material in `config.system_instruction`. Put the compressed text there; the user's actual question stays in `contents`.

```python
from google import genai

genai_client = genai.Client()  # reads GEMINI_API_KEY

compressed = client.compress(
    context=long_document,
    query=user_question,
    compression_model_name="{{DEFAULT_MODEL}}",
).data.compressed_context

response = genai_client.models.generate_content(
    model="gemini-2.5-flash",
    contents=user_question,  # string shortcut requires google-genai>=0.3.0
    config={"system_instruction": compressed},
)
print(response.text)
```

**TypeScript**

```typescript
import { GoogleGenAI } from '@google/genai';

const genai = new GoogleGenAI({}); // reads GEMINI_API_KEY

const { data } = await client.compress({
  context: longDocument,
  query: userQuestion,
  compressionModelName: '{{DEFAULT_MODEL}}',
});

const response = await genai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: userQuestion,
  config: { systemInstruction: data.compressed_context },
});
console.log(response.text);
```

**cURL**

```bash
# 1. Compress.
COMPRESSED=$(curl -s -X POST https://api.compresr.ai/api/compress/question-specific/ \
  -H "X-API-Key: $COMPRESR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "context": "<long document>",
    "query": "<user question>",
    "compression_model_name": "{{DEFAULT_MODEL}}"
  }' | jq -r '.data.compressed_context')

# 2. Pass to Gemini.
curl -X POST "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=$GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"system_instruction\": {\"parts\": [{\"text\": $(jq -Rs . <<< \"$COMPRESSED\")}]},
    \"contents\": [{\"role\": \"user\", \"parts\": [{\"text\": \"<user question>\"}]}]
  }"
```

Gemini uses a SentencePiece-style tokenizer. The Compresr response's tiktoken counts are useful as a ratio but won't match Gemini's billing exactly; for accurate counts call `genai_client.models.count_tokens(model="gemini-2.5-flash", contents=compressed)` on the compressed string. A 1M-token context window does not make a 1M-token request free.

Ollama runs open-weight models locally over a small HTTP API. The shape is the same as OpenAI's chat completions, `system` + `user` messages, just pointed at `localhost`.

```python
import ollama

compressed = client.compress(
    context=long_document,
    query=user_question,
    compression_model_name="{{DEFAULT_MODEL}}",
).data.compressed_context

response = ollama.chat(
    model="llama3.2",
    messages=[
        {"role": "system", "content": compressed},
        {"role": "user", "content": user_question},
    ],
)
print(response["message"]["content"])
```

**TypeScript**

```typescript
import { Ollama } from 'ollama';

const ollama = new Ollama();

const { data } = await client.compress({
  context: longDocument,
  query: userQuestion,
  compressionModelName: '{{DEFAULT_MODEL}}',
});

const response = await ollama.chat({
  model: 'llama3.2',
  messages: [
    { role: 'system', content: data.compressed_context },
    { role: 'user', content: userQuestion },
  ],
});
console.log(response.message.content);
```

**cURL**

```bash
# 1. Compress.
COMPRESSED=$(curl -s -X POST https://api.compresr.ai/api/compress/question-specific/ \
  -H "X-API-Key: $COMPRESR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "context": "<long document>",
    "query": "<user question>",
    "compression_model_name": "{{DEFAULT_MODEL}}"
  }' | jq -r '.data.compressed_context')

# 2. Pass to local Ollama.
curl -X POST http://localhost:11434/api/chat \
  -H "Content-Type: application/json" \
  -d "{
    \"model\": \"llama3.2\",
    \"stream\": false,
    \"messages\": [
      {\"role\": \"system\", \"content\": $(jq -Rs . <<< \"$COMPRESSED\")},
      {\"role\": \"user\", \"content\": \"<user question>\"}
    ]
  }"
```

Llama-family tokenizers count differently from tiktoken, so the savings ratio still applies but exact token math diverges. The bigger wins on local hardware: shorter prefill (faster first token on CPU/single-GPU) and less KV-cache pressure, leaving room for longer generations on the same machine.

## When this helps

- **Token-cost wins, every provider.** Hosted providers bill per input token. Compressing a 20k-token RAG payload by 60–80% lands directly on your bill, every call.
- **Headroom inside the context window.** Long retrieval pipelines or accumulated chat history can crowd even 200k+ windows; compression buys room for the model's own reasoning output.
- **Lower time-to-first-token.** Prefill is roughly linear in input length. Fewer input tokens means the model starts generating sooner; noticeable on chat UIs and agent loops.

## Notes

Compressed context belongs in the slot the provider treats as reference material: `system` for OpenAI/Anthropic/Ollama, `system_instruction` for Gemini. The user's actual question stays in the user-role slot. Don't compress short, structured system prompts (instructions, output schemas, tool definitions). Compresr is designed for long, semi-redundant retrieved or accumulated context, not for prompts you wrote by hand.

For parameter semantics (`target_compression_ratio`, `coarse`, and friends) see [models](/docs/api-reference/models). For handling rate-limit and timeout failures from this two-call chain, see [errors](/docs/api-reference/errors).
