RTX 3050 - Order Now
Home / Blog / LLM Hosting / LLM Context Window: Sliding Strategy
LLM Hosting

LLM Context Window: Sliding Strategy

Manage LLM context window limits with sliding window strategies. Covers message truncation, summarisation, token counting, priority retention, and memory-efficient conversation handling on GPU servers.

Your Conversation Exceeds the Model’s Context Window

After 15 turns of conversation, your LLM API returns an error: the input exceeds the model’s maximum context length. Or worse, it silently truncates early messages and the model loses track of what the user said at the beginning. A Llama 3.1 8B with an 8K context fills up quickly once you add a system prompt, conversation history, and a document for reference. Managing context on your GPU server requires a deliberate strategy — not just hoping conversations stay short.

Accurate Token Counting

Before implementing a sliding window, count tokens accurately for your specific model:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3.1-8B-Instruct")

def count_tokens(messages):
    """Count tokens for a chat message list."""
    total = 0
    for msg in messages:
        # Each message has role/content overhead (model-specific)
        total += len(tokenizer.encode(msg["content"]))
        total += 4  # Approximate overhead for role tags
    total += 2  # Start/end tokens
    return total

# Example
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Tell me about GPU computing."},
    {"role": "assistant", "content": "GPU computing uses parallel..."},
]
print(f"Token count: {count_tokens(messages)}")

# Budget allocation for 8192-token context:
# System prompt:     ~500 tokens (fixed)
# Max response:     ~1000 tokens (reserved for generation)
# Conversation:     ~6692 tokens (available for history)

Basic Sliding Window

Drop the oldest messages when the conversation exceeds the token budget:

def sliding_window(messages, max_tokens=8192, reserved_for_response=1000):
    available = max_tokens - reserved_for_response

    # Always keep the system message
    system_msgs = [m for m in messages if m["role"] == "system"]
    conversation = [m for m in messages if m["role"] != "system"]

    system_tokens = count_tokens(system_msgs)
    budget = available - system_tokens

    # Keep messages from newest to oldest until budget exhausted
    kept = []
    running_tokens = 0
    for msg in reversed(conversation):
        msg_tokens = count_tokens([msg])
        if running_tokens + msg_tokens > budget:
            break
        kept.insert(0, msg)
        running_tokens += msg_tokens

    return system_msgs + kept

# Usage
trimmed = sliding_window(full_conversation, max_tokens=8192)
response = call_llm(trimmed)

Smart Truncation with Summarisation

Instead of dropping old messages entirely, summarise them to preserve context:

async def summarise_and_slide(messages, max_tokens=8192):
    system_msgs = [m for m in messages if m["role"] == "system"]
    conversation = [m for m in messages if m["role"] != "system"]

    if count_tokens(messages) <= max_tokens:
        return messages  # No truncation needed

    # Split conversation into old and recent halves
    midpoint = len(conversation) // 2
    old_messages = conversation[:midpoint]
    recent_messages = conversation[midpoint:]

    # Summarise old messages using the LLM itself
    summary_prompt = [
        {"role": "system", "content": "Summarise this conversation in 2-3 sentences. "
         "Include key facts, decisions, and user preferences."},
        *old_messages
    ]
    summary = await call_llm(summary_prompt, max_tokens=200)

    # Reconstruct with summary replacing old messages
    return system_msgs + [
        {"role": "system", "content": f"Previous conversation summary: {summary}"},
        *recent_messages
    ]

Priority-Based Message Retention

Not all messages are equally important. Keep high-value messages while dropping filler:

def priority_window(messages, max_tokens=8192, reserved=1000):
    system_msgs = [m for m in messages if m["role"] == "system"]
    conversation = [m for m in messages if m["role"] != "system"]
    budget = max_tokens - reserved - count_tokens(system_msgs)

    # Assign priority scores
    scored = []
    for i, msg in enumerate(conversation):
        score = 0
        score += 10 if i >= len(conversation) - 4 else 0  # Recent messages
        score += 5 if msg["role"] == "user" else 3         # User msgs over assistant
        score += 3 if "?" in msg["content"] else 0         # Questions
        score += 2 if len(msg["content"]) > 200 else 0     # Substantial messages
        scored.append((score, i, msg))

    # Sort by priority, take highest-scoring messages within budget
    scored.sort(key=lambda x: (-x[0], x[1]))
    kept = []
    tokens_used = 0
    for score, idx, msg in scored:
        msg_tokens = count_tokens([msg])
        if tokens_used + msg_tokens <= budget:
            kept.append((idx, msg))
            tokens_used += msg_tokens

    # Restore chronological order
    kept.sort(key=lambda x: x[0])
    return system_msgs + [msg for _, msg in kept]

Putting It Together in Production

class ConversationManager:
    def __init__(self, model_max_tokens=8192, response_reserve=1000):
        self.max_tokens = model_max_tokens
        self.reserve = response_reserve
        self.conversations = {}  # session_id -> messages

    async def add_and_generate(self, session_id, user_message):
        if session_id not in self.conversations:
            self.conversations[session_id] = [
                {"role": "system", "content": SYSTEM_PROMPT}
            ]

        self.conversations[session_id].append(
            {"role": "user", "content": user_message})

        # Apply sliding window if needed
        messages = await summarise_and_slide(
            self.conversations[session_id], self.max_tokens)

        response = await call_llm(messages, max_tokens=self.reserve)
        self.conversations[session_id].append(
            {"role": "assistant", "content": response})

        return response

Context window management is critical for vLLM and Ollama deployments on your GPU server. The vLLM production guide covers max-model-len configuration. See the LLM hosting section for architecture patterns, tutorials for implementation guides, and benchmarks for context-length performance data.

Long-Context LLM Servers

GigaGPU dedicated servers with high-VRAM GPUs support extended context windows. More memory, longer conversations.

Browse GPU Servers

Need a Dedicated GPU Server?

Deploy from RTX 3050 to RTX 5090. Full root access, NVMe storage, 1Gbps — UK datacenter.

Browse GPU Servers

gigagpu

We benchmark, deploy, and optimise GPU infrastructure for AI workloads. All data in our guides comes from real-world testing on our UK-based dedicated GPU servers.

Ready to deploy your AI workload?

Dedicated GPU servers from our UK datacenter. NVMe storage, 1Gbps networking, full root access.

Browse GPU Servers Contact Sales

Have a question? Need help?