Skip to main content

Intelligent LRU context manager for LLM agents โ€” scores chunks by relevance, recency, and access frequency. Keeps hot context, evicts cold. Drop-in for Strands, LangChain, and OpenAI.

Project description

๐Ÿ”ฅ hot-context

LRU-based intelligent context manager for LLM agents. Keeps hot context, evicts cold.

Most conversation managers use a naive sliding window โ€” drop the oldest messages when context fills up. hot-context treats your context window like an intelligent cache: every chunk is scored by relevance, recency, and access frequency, and only the highest-value context survives.

PyPI version License [arXiv](Coming Soon)


Key Features

  • Relevance-aware trimming โ€” trims tool results to only the lines the LLM actually used, instead of evicting entire entries
  • Adaptive token calibration โ€” learns the real chars-per-token ratio from actual LLM feedback, self-corrects over time
  • Entity echo feedback loop โ€” uses the LLM's own response as a proxy for attention weights, zero extra API calls
  • Turn-aware eviction โ€” never evicts during a turn (LLM needs full context to reason), only trims between turns
  • Framework-agnostic core with drop-in adapters for Strands, LangChain, and OpenAI

How It Works

Turn N:
  User query โ†’ Tools return data โ†’ LLM sees FULL context โ†’ Answers
                                                              โ†“
                                          Entity echo: what did the LLM use?
                                          Trimmer: keep only relevant lines from tool results
                                          Calibrator: learn real token ratio from Bedrock
                                                              โ†“
Turn N+1:
  Context has trimmed tool results (5k instead of 80k) + all assistant answers preserved

Scoring formula (no LLM needed):

score = ฮฑยทsimilarity(chunk, query) + ฮฒยทrecency(chunk) + ฮณยทaccess_frequency(chunk) - ฮดยทsize_penalty(chunk)
  • Similarity: Jaccard overlap on extracted entities (BM25-inspired, zero API calls)
  • Recency: Exponential decay by turn distance
  • Access frequency: Time-decayed count of how often the LLM actually used this chunk
  • Size penalty: Large tool results are penalized proportionally to budget

Topic shift detection: When the user changes topics, similarity weight increases and frequency weight decreases โ€” preventing stale popular chunks from dominating.


Results

Tested against Strands SlidingWindowConversationManager on a real multi-turn agent with MCP tools (SQL queries, knowledge base lookups):

Metric SlidingWindow hot-context
Q3 (heavy query) CRASHED โ€” context overflow โœ… Answered (2831 chars)
Peak context Q2 Unbounded (168k+) 105k (trimmed)
Peak context Q3 Overflow (>200k) 113k (controlled)
Queries completed 2.5 of 3 3 of 3

Peak context went DOWN from Q1โ†’Q2 (128kโ†’105k) โ€” the trimmer removed irrelevant lines from Q1's tool result.


Quick Start

Installation

# Core โ€” no dependencies
pip install hot-context

# With AWS Strands adapter
pip install hot-context[strands]

# With LangChain adapter
pip install hot-context[langchain]

# With embedding-based similarity (recommended for production)
pip install hot-context[embeddings]

# Everything
pip install hot-context[all]

Strands (drop-in replacement)

from strands import Agent
from strands.models.bedrock import BedrockModel
from hot_context.adapters.strands import StrandsHotContextManager
from hot_context import ScoringWeights

agent = Agent(
    model=BedrockModel(model_id="us.anthropic.claude-sonnet-4-5-20250929-v1:0"),
    conversation_manager=StrandsHotContextManager(
        token_budget=100_000,
        weights=ScoringWeights.tool_agent(),
    ),
)

LangChain (drop-in replacement)

from langchain_core.runnables import RunnableWithMessageHistory
from hot_context.adapters.langchain import HotContextChatHistory
from hot_context import ScoringWeights

history = HotContextChatHistory(
    token_budget=50_000,
    weights=ScoringWeights.conversational(),
)

chain_with_history = RunnableWithMessageHistory(
    runnable=your_chain,
    get_session_history=lambda session_id: history,
)

Framework-agnostic core

from hot_context import HotContextManager, ScoringWeights

manager = HotContextManager(
    token_budget=100_000,
    weights=ScoringWeights.tool_agent(),
    pin_last_n=2,
)

# Before each query
manager.on_query(user_input)

# After LLM responds
manager.on_response(str(result))

Adaptive calibration (Strands)

# Token estimator self-calibrates from actual Bedrock token counts.
# After each LLM call, feed back the reported input_tokens:
cm = agent.conversation_manager
cm.calibrate(agent.messages, actual_input_tokens=87450)
# Future estimates are now more accurate โ€” no manual tuning needed.

Weight Presets

Use a built-in preset or define your own custom weights:

ScoringWeights.tool_agent()     # high similarity โ€” for agents with many tool calls
ScoringWeights.conversational() # high recency + frequency โ€” for multi-turn chat
ScoringWeights.research()       # high similarity + frequency โ€” for RAG/research agents

# Custom weights โ€” all four components must sum to 1.0
ScoringWeights(
    similarity=0.5,    # how much query-chunk relevance matters
    recency=0.2,       # how much to favor recent turns
    access_freq=0.2,   # how much to favor chunks the LLM has used before
    size_penalty=0.1,  # how much to penalize large chunks
)

Token Budget

token_budget is the maximum tokens reserved for conversation history. It should be sized as:

token_budget = model_context_limit - expected_query_tokens - system_prompt_tokens - tool_definition_tokens

For example, with Claude's 200k context window:

HotContextManager(
    token_budget=100_000,  # leaves headroom for prompts, tools, and the response
)

Setting it too high risks context overflow; too low and hot-context will aggressively evict useful history.

pin_last_n

pin_last_n protects the most recent N turns from eviction, ensuring the LLM always has immediate conversational context regardless of score:

HotContextManager(
    pin_last_n=2,  # last 2 turns are never evicted (default)
)

Increase this if your agent relies heavily on recent tool results that shouldn't be trimmed mid-conversation.

Custom Entity Patterns

import re
from hot_context import HotContextManager

manager = HotContextManager(
    extra_patterns=[
        ("order_id", re.compile(r"\b\d{3}-\d{7}-\d{7}\b")),
        ("asin", re.compile(r"\bB[A-Z0-9]{9}\b")),
    ]
)

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    HotContextManager                         โ”‚
โ”‚                                                              โ”‚
โ”‚  TokenEstimator โ”€โ”€โ†’ EntityExtractor โ”€โ”€โ†’ ContextScorer        โ”‚
โ”‚       โ†‘                                      โ”‚               โ”‚
โ”‚  calibrate()                            score + rank         โ”‚
โ”‚  (from LLM)                                  โ”‚               โ”‚
โ”‚                                              โ†“               โ”‚
โ”‚  Trimmer โ†โ”€โ”€ FeedbackLoop โ†โ”€โ”€ LLM Response โ†โ”€โ”€ Evictor      โ”‚
โ”‚  (line-level    (entity echo)                (entry-level)   โ”‚
โ”‚   relevance)                                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Adapters: Strands | LangChain | OpenAI

Why Not Just Summarize?

Approach Token cost Latency Quality
Sliding window Free None Poor โ€” drops relevant old context
LLM summarization High +500ms/turn Good โ€” but expensive at scale
hot-context Free ~1ms/turn Good โ€” keeps what matters, trims what doesn't

Components

File Purpose
manager.py Core orchestrator: on_query() โ†’ score + evict, on_response() โ†’ feedback
scorer.py Scoring formula with topic shift detection and pluggable similarity
token_estimator.py Adaptive chars-per-token calibration from real LLM feedback
trimmer.py Line-level relevance trimming of tool results
chunker.py Smart chunking respecting tool-use/tool-result pairing
entity_extractor.py Regex-based entity extraction (dates, IDs, numbers, custom patterns)
feedback.py Entity echo feedback loop โ€” updates access logs from LLM response
types.py ContextEntry, AccessRecord, ScoringWeights with presets
adapters/strands.py Drop-in ConversationManager for Strands agents
adapters/langchain.py BaseChatMessageHistory adapter for LangChain
adapters/openai.py Message list manager for OpenAI Chat API

Roadmap

P2 โ€” Benchmarks & Paper

  • Benchmark harness โ€” synthetic multi-turn conversations with known answers. Measure token savings + answer quality (ROUGE/BERTScore on trimmed vs full context)
  • Standard benchmarks โ€” LongBench, SCROLLS, QuALITY, NarrativeQA for long-context evaluation
  • Ablation studies โ€” trimmer-only vs scorer-only vs full pipeline vs baselines (MemGPT, LLMLingua, attention sink)
  • Token savings curves โ€” plot peak context vs conversation length across strategies

Contributing

Issues and PRs welcome at github.com/Ankit28P/hot-context.

License

Apache 2.0 โ€” 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

hot_context-0.1.2.tar.gz (33.4 kB view details)

Uploaded Source

Built Distribution

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

hot_context-0.1.2-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file hot_context-0.1.2.tar.gz.

File metadata

  • Download URL: hot_context-0.1.2.tar.gz
  • Upload date:
  • Size: 33.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hot_context-0.1.2.tar.gz
Algorithm Hash digest
SHA256 8e3fe16e7f98cdc8fc3f2f83bfbe0a6bd32c0ed655dbe9b9fd77ee1b58840996
MD5 f59ea69357620dcfbd1198e3244a4100
BLAKE2b-256 30279cda083b4c3071181ab1f1076aa6be525a1d48cbecaad13e3d23fb3c6dc3

See more details on using hashes here.

Provenance

The following attestation bundles were made for hot_context-0.1.2.tar.gz:

Publisher: publish.yml on Ankit28P/hot-context

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hot_context-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: hot_context-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 27.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hot_context-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 737c1bf42af1cf3efb49ba8be010b6d0ac372fa8027444880c808d298e2bba38
MD5 3c212c5407c5e11751b94765481ebb42
BLAKE2b-256 0bc80963781087b7990c587d3b08426a6013e7ae2b52db6b3ba7634d7c232ed2

See more details on using hashes here.

Provenance

The following attestation bundles were made for hot_context-0.1.2-py3-none-any.whl:

Publisher: publish.yml on Ankit28P/hot-context

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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