Skip to main content

Fit chat history into any model's context window: token estimation, sliding-window packing with a running summary, and overflow detection. Zero dependencies, no AI inside.

Project description

llm-context-budget

PyPI license

Fit chat history into any model's context window.

Every chat app eventually writes the same code: estimate tokens, keep recent turns verbatim, compress the old ones, reserve room for the response, and handle the overflow error anyway. This package is that code — extracted from a production on-device assistant where the context windows are small and an overflow doesn't return a 400, it crashes the runtime.

Zero dependencies. No AI inside — the summarizer is a callback you can point at your model, with a deterministic extractive fallback built in. Messages are plain dicts in the OpenAI/Anthropic shape, multimodal content included. Python sibling of @yanib/context-budget (npm).

from context_budget import pack_messages

result = pack_messages(
    history,                                   # [{"role": ..., "content": ...}, ...]
    context_tokens=8192,                       # your model's window
    response_reserve=600,                      # headroom for the reply
    summary=conversation.summary,              # carried from the last turn
    already_summarized=conversation.cursor,    # ...so old turns aren't re-folded
    system_blocks=[persona_prompt, rag_context],
)

reply = client.chat.completions.create(model=..., messages=result.messages)
conversation.summary = result.summary          # persist for next turn
conversation.cursor = result.summarized_count

Async apps use apack_messages, which also accepts async summarizers:

result = await apack_messages(
    history,
    summarize=lambda prev, dropped: my_model_summary(prev, dropped),  # sync or async
)

Install

pip install llm-context-budget

The import name is context_budget — matching the JS sibling. (PyPI's name-similarity rules block both context-budget and token-budget as distribution names; the code doesn't care.)

How it packs

  1. The most recent turns stay verbatim (window size adapts to the model: 8 turns at 4k context, 16 at 8k, 24 at 16k+ — or set your own).
  2. Older turns are represented by a running summary, carried between turns via summary + summarized_count so nothing is summarized twice.
  3. If the verbatim window still blows the budget, it's trimmed from the front and the trimmed turns are folded into the summary — one summarizer call per pack, not one per message.
  4. Final guard: if a single message + system blocks still overflow, the summary is hard-truncated into whatever room remains. If even that can't fit, you get fits=False and you decide (the messages are still returned).

System blocks and the summary are merged into one system message — some providers accept only a single instructions block, and every other provider tolerates it.

Token estimation

The default estimator is the ~4-chars-per-token heuristic — deliberately dependency-free (real tokenizers cost megabytes and vary per model, and budgets carry headroom anyway). Have exact counts? Plug them in:

import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
pack_messages(history, estimate_tokens=lambda t: len(enc.encode(t)))

Also exported: create_char_estimator(ratio), truncate_to_tokens(text, max, estimator, keep="head"|"tail"), estimate_tokens_of, context_limits_for, content_text.

The overflow retry

Estimates are estimates. When the provider still throws, detect it and retry aggressively — half the verbatim window:

from context_budget import is_context_overflow_error, pack_messages

try:
    return call_model(result.messages)
except Exception as err:
    if not is_context_overflow_error(err):
        raise
    retry = pack_messages(history, aggressive=True, **options)
    return call_model(retry.messages)

The summarizer seam

summarize(previous_summary, dropped_turns) may call anything:

  • Default: create_extractive_summarizer() — one compact labeled line per dropped turn, capped total size keeping the tail. Deterministic, instant, offline.
  • Your model: semantic summaries when quality matters. If your call fails, return previous_summary — a memory hiccup should never block a chat turn.

Extra message keys (ids, tool calls, timestamps) pass through packing untouched, and OpenAI-style multimodal list content is estimated by its text parts.

API

pack_messages(history, *, context_tokens=4096, response_reserve=600,
              recent_window=None, aggressive=False, estimate_tokens=...,
              summary="", already_summarized=0, system_blocks=(),
              summarize=None) -> PackResult

PackResult:
    messages          # one merged system message (if any) + verbatim window
    summary           # persist and pass back next turn
    summarized_count  # pass back as already_summarized next turn
    dropped           # turns folded into the summary this call
    used_tokens, budget, fits

Fully typed (py.typed), Python 3.9+.

License

MIT © Binaya Dhakal

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

llm_context_budget-0.1.0.tar.gz (10.5 kB view details)

Uploaded Source

Built Distribution

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

llm_context_budget-0.1.0-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: llm_context_budget-0.1.0.tar.gz
  • Upload date:
  • Size: 10.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.7

File hashes

Hashes for llm_context_budget-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d378da17e19f156f2a614a36b24af094b851984b1f1902a34297657520b8a1de
MD5 353ae2377b339919c514af4c10f64623
BLAKE2b-256 c858835c3cf6a9d367d97979563d5c64c9f3f1eb351f642638a8e11456d4a8aa

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for llm_context_budget-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4afaafd0763d88a60f4b550824a18f5b3bc7ab1241f093bf9b8f57ed263a67f5
MD5 77028ac449b74d19f46038b6ad902934
BLAKE2b-256 d4d7993388703000edf66e29878bd7a358a2d90c841141c6c3e75559ac46393c

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