{"id":1095,"date":"2026-09-07T09:30:00","date_gmt":"2026-09-06T23:30:00","guid":{"rendered":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/"},"modified":"2026-11-03T20:27:55","modified_gmt":"2026-11-03T09:27:55","slug":"local-llm-context-budget","status":"publish","type":"post","link":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/","title":{"rendered":"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11)"},"content":{"rendered":"\n<p class=\"has-pale-cyan-blue-background-color has-background wp-block-paragraph\">On a local AI server, a prompt too big for the model gets silently truncated, and raising the window for every parallel slot is what froze our lab server. We built admission control and gateway-side compaction, then found our token counts were out by up to 322 times.<\/p>\n\n\n\n<div class=\"wp-block-group has-background\" style=\"background-color:#f4f6f8;padding-top:28px;padding-right:32px;padding-bottom:28px;padding-left:32px\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-40de7cf3 wp-block-group-is-layout-constrained\">\n\n<p class=\"wp-block-paragraph\"><strong>Where this fits \u00b7 Running Local AI in Production \u00b7 Part 4 of 11<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" style=\"font-size:17px\">You are reading Part 4 of 11 in a series where we built a private AI server in our lab and ran realistic test workloads on it for seven weeks. Each part stands on its own. The previous part covered five hard freezes and the memory budget that ended them; this one covers fitting every prompt to a local model&#8217;s context window.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\" style=\"font-size:16px\"><a href=\"https:\/\/catharsis.net.au\/blog\/local-ai-server-freezes-unified-memory\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u2190 Part 3: Why Does a Local AI Server Freeze? Sizing Memory for LLMs<\/a> &nbsp;\u00b7&nbsp; <a href=\"https:\/\/catharsis.net.au\/blog\/running-local-ai-in-production-series-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Series guide<\/a> &nbsp;\u00b7&nbsp; <a href=\"https:\/\/catharsis.net.au\/blog\/smart-routing-single-gpu-local-llm\/\" target=\"_blank\" rel=\"noreferrer noopener\">Part 5: How Do You Run Several AI Models on One GPU Without Slowdowns? \u2192<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" style=\"font-size:16px\">Just want it working? Follow the <a href=\"https:\/\/catharsis.net.au\/blog\/local-ai-litellm-setup-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">step-by-step setup guide<\/a>: local AI with Ollama and LiteLLM, from a bare server to a secure gateway.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>See all 11 parts of the series<\/summary>\n\n<ol class=\"wp-block-list\">\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/why-run-ai-on-your-own-hardware\/\" target=\"_blank\" rel=\"noreferrer noopener\">Local AI or Cloud AI? Why We Built a Private AI Server<\/a>: why we built the lab on our own hardware, what ran on it and what that choice cost<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/llm-gateway-one-door-for-every-model\/\" target=\"_blank\" rel=\"noreferrer noopener\">How Do You Control Access to Local AI Models? Use a Gateway<\/a>: one LLM gateway as the only way to reach a model: keys, budgets and tracing<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/local-ai-server-freezes-unified-memory\/\" target=\"_blank\" rel=\"noreferrer noopener\">Why Does a Local AI Server Freeze? Sizing Memory for LLMs<\/a>: five hard freezes and the memory budget that ended them<\/li>\n\n\n<li><strong>What Happens When a Prompt Is Too Big for a Local LLM?<\/strong> (you are here): fitting every prompt to a local model&#8217;s context window<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/smart-routing-single-gpu-local-llm\/\" target=\"_blank\" rel=\"noreferrer noopener\">How Do You Run Several AI Models on One GPU Without Slowdowns?<\/a>: routing requests across models on a single GPU without constant model swaps<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/gitops-for-a-gpu-ai-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">How Do You Keep an AI Server Stable? GitOps for Local AI<\/a>: running the server from a Git repository: deploys, drift, pinning and safe AI agents<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/observability-for-local-ai\/\" target=\"_blank\" rel=\"noreferrer noopener\">What Is Your Local AI Server Actually Doing? Observability Basics<\/a>: making the server observable: traces, dashboards, usage and heat<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/multi-agent-stock-analysis-local-llm\/\" target=\"_blank\" rel=\"noreferrer noopener\">Can Local AI Agents Analyse Stocks? How We Tested and Scored Them<\/a>: a multi-agent stock-analysis experiment and how we scored the agents<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/telegram-ai-agent-local-llms\/\" target=\"_blank\" rel=\"noreferrer noopener\">Why Was Our Telegram AI Agent So Slow? The Hidden Prompt Cost<\/a>: a Telegram AI agent on local models, and why its fixed prompt became the budget<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/local-ai-business-workflows\/\" target=\"_blank\" rel=\"noreferrer noopener\">Can AI Automate Business Workflows Without Sending Data Out?<\/a>: document, email and knowledge-base workflows that keep data on the server<\/li>\n\n\n<li><a href=\"https:\/\/catharsis.net.au\/blog\/private-ai-lab-lessons-and-whats-next\/\" target=\"_blank\" rel=\"noreferrer noopener\">Is Private AI Worth It? Lessons From Seven Weeks in Our Lab<\/a>: the lessons from seven weeks, the honest limits and what comes next<\/li>\n\n<\/ol>\n\n<\/details>\n\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\" style=\"font-size:16px\"><em>This series documents a private AI server we built and stress-tested in our lab. Workloads and data are synthetic or generalised; the problems, measurements and fixes are real.<\/em><\/p>\n\n\n\n<div class=\"wp-block-group has-background\" style=\"background-color:#f4f6f8;padding-top:28px;padding-right:32px;padding-bottom:28px;padding-left:32px\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-40de7cf3 wp-block-group-is-layout-constrained\">\n\n<h3 class=\"wp-block-heading\">TL;DR<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>By default, the model cuts it silently and answers anyway. Ollama drops whatever doesn&#8217;t fit the context window, so a chat loses its start and an agent loses its own reasoning, and raising the window, with four parallel slots each reserving it, is what froze the lab server in <a href=\"https:\/\/catharsis.net.au\/blog\/local-ai-server-freezes-unified-memory\/\" target=\"_blank\" rel=\"noreferrer noopener\">Part 3<\/a>.<\/li>\n\n\n<li>We put an admission check in the gateway that counts every prompt and refuses oversized ones with a clear error, then added compaction so long agent conversations keep working instead of stopping.<\/li>\n\n\n<li>Shipping it took three rounds of soak test and rollback. The token counter turned out to be wrong by up to 322 times, and one compaction defect cost about 90 times the GPU time per turn.<\/li>\n\n\n<li>The lesson: enforce the budget in one place, count the way the model counts, and never estimate tokens from characters.<\/li>\n\n<\/ul>\n\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-group has-background\" style=\"background-color:#f4f6f8;padding-top:28px;padding-right:32px;padding-bottom:28px;padding-left:32px\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-40de7cf3 wp-block-group-is-layout-constrained\">\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Jargon buster: 15 terms used in this post, explained in plain English<\/summary>\n\n<ul class=\"wp-block-list\">\n\n<li><strong>Context window<\/strong>: The maximum number of tokens a model can consider at once, covering both the prompt and its answer.<\/li>\n\n\n<li><strong>Slot (parallel requests)<\/strong>: One request a model server can work on at the same time. More slots serve more people at once but reserve more memory.<\/li>\n\n\n<li><strong>Ollama<\/strong>: Free, open-source software that downloads and runs AI models on your own machine and answers requests over a local API.<\/li>\n\n\n<li><strong>LiteLLM<\/strong>: Open-source software that puts one front door in front of many AI models, with per-user keys, budgets, limits and logging.<\/li>\n\n\n<li><strong>LLM gateway<\/strong>: A single service every AI request must pass through, so access, budgets and logging are controlled in one place.<\/li>\n\n\n<li><strong>Trace<\/strong>: A detailed record of one request as it passes through each part of a system, used to find where time or errors come from.<\/li>\n\n\n<li><strong>Embedding<\/strong>: A list of numbers that represents the meaning of a piece of text, so similar texts can be found by comparing numbers.<\/li>\n\n\n<li><strong>Chunking<\/strong>: Splitting long documents into smaller passages before creating embeddings, so the right part can be found and fits in the context window.<\/li>\n\n\n<li><strong>Tool schema<\/strong>: The description of a tool (its name, purpose and inputs) that is sent to the model with every request so it knows the tool exists. Many tools mean a long prompt.<\/li>\n\n\n<li><strong>Pre-commit check<\/strong>: An automatic check that runs before a change is saved to the repository and blocks it if it breaks a rule.<\/li>\n\n\n<li><strong>Loopback<\/strong>: A network address that only the server itself can reach, so a service listening there isn&#8217;t exposed to the network.<\/li>\n\n\n<li><strong>Soak test<\/strong>: Running a system under steady, realistic load for a while to see whether problems appear over time.<\/li>\n\n\n<li><strong>Compaction<\/strong>: Shortening a long conversation (for example by summarising older messages) so it fits the model&#8217;s context window.<\/li>\n\n\n<li><strong>Admission control<\/strong>: Checking a request before it reaches the model and refusing it with a clear error if it&#8217;s too big or not allowed.<\/li>\n\n\n<li><strong>HTTP status codes (400, 413, 429)<\/strong>: Standard codes a server returns: 400 means a bad request, 413 means it&#8217;s too large, 429 means too many requests.<\/li>\n\n<\/ul>\n\n<\/details>\n\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-group has-background\" style=\"background-color:#f4f6f8;padding-top:28px;padding-right:32px;padding-bottom:28px;padding-left:32px\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-40de7cf3 wp-block-group-is-layout-constrained\">\n\n<h3 class=\"wp-block-heading\">In this post<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li><a href=\"#the-problem-what-happens-to-a-prompt-that-doesn-t-fit\">The problem: what happens to a prompt that doesn&#8217;t fit?<\/a><\/li>\n\n\n<li><a href=\"#what-we-were-aiming-for\">What we were aiming for<\/a><\/li>\n\n\n<li><a href=\"#how-we-approached-it\">How we approached it<\/a><\/li>\n\n\n<li><a href=\"#what-we-built\">What we built<\/a><\/li>\n\n\n<li><a href=\"#challenges-we-hit\">Challenges we hit<\/a><\/li>\n\n\n<li><a href=\"#the-result\">The result<\/a><\/li>\n\n\n<li><a href=\"#do-it-yourself-an-admission-check-in-litellm\">Do it yourself: an admission check in LiteLLM<\/a><\/li>\n\n\n<li><a href=\"#key-takeaways\">Key takeaways<\/a><\/li>\n\n<\/ul>\n\n<\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-problem-what-happens-to-a-prompt-that-doesn-t-fit\">The problem: what happens to a prompt that doesn&#8217;t fit?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every model has a <strong>context window<\/strong>, the most text it can hold at once, counting the prompt and the answer together. Text is measured in tokens, words or parts of words, about four characters of English prose. After the freeze described in <a href=\"https:\/\/catharsis.net.au\/blog\/local-ai-server-freezes-unified-memory\/\" target=\"_blank\" rel=\"noreferrer noopener\">Part 3<\/a>, each model in the lab got a 32,768-token window and one parallel slot, so one request gets 32,768 tokens in total. We can&#8217;t make that bigger without risking another freeze.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When a request goes over, Ollama, the server that runs the models, logs <code>truncating input prompt<\/code> and answers anyway. A chat loses its opening. An agent loses the middle of its own reasoning and carries on as if nothing happened. Embeddings were worse: a 9,071-token request came back as a success with <code>prompt_tokens=2048<\/code>, because the embedding model had quietly used only the first 2,048 tokens.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That matters because the answer looks fine. A confident reply to a prompt the model never fully saw is harder to catch than an error, and long agent runs are exactly where prompts grow largest.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-we-were-aiming-for\">What we were aiming for<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>No prompt is ever truncated silently. Anything that doesn&#8217;t fit is refused before it reaches the GPU, with an error that says why.<\/li>\n\n\n<li>Long agent conversations keep working, without losing the tool results the agent depends on.<\/li>\n\n\n<li>Token counts match what the model really sees, so the budget means something.<\/li>\n\n\n<li>All of this lives in one place, the gateway, so no client has to get it right on its own.<\/li>\n\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"how-we-approached-it\">How we approached it<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every model call in the lab already goes through one gateway, LiteLLM (<a href=\"https:\/\/catharsis.net.au\/blog\/llm-gateway-one-door-for-every-model\/\" target=\"_blank\" rel=\"noreferrer noopener\">Part 2<\/a>), so that is where the check belonged. LiteLLM can enforce context limits itself, but by default it treats <code>max_input_tokens<\/code> as model metadata and only acts on it if you turn on its pre-call checks (<code>enable_pre_call_checks<\/code>), which filter out deployments too small for a request. We wanted our own count, a clear error and a place to plug in compaction, so we wrote our own admission check.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For compaction we looked at LiteLLM&#8217;s built-in options. <code>trim_messages()<\/code> simply drops messages until the prompt fits, and the other route relies on a provider-side compaction API that Ollama doesn&#8217;t offer. Cloud agent platforms keep the working set and carry the rest forward in shorter form, so we built that ourselves, as a separate service the gateway calls only when a request is over budget.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-we-built\">What we built<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">An admission check in the gateway<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A small Python module that LiteLLM loads as a callback counts each request&#8217;s tokens before it is routed. Over budget, it returns an OpenAI-shaped <code>400<\/code> with <code>code: context_length_exceeded<\/code>, naming the count, the budget and what to do. The budget is 26,000 tokens on a 32,768 slot, which leaves at least 6,768 for the reply. Embedding calls are checked too, per input rather than per batch.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A token count that matches the model<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The gate counts tool definitions, counts digits the way Qwen and Gemma do, and adds the chat template&#8217;s overhead for each message. On top of that each model gets a measured safety margin, declared next to its budget: 10% for Qwen, 25% for Gemma and 25% for any model we haven&#8217;t measured. If the tokenizer fails, the fallback is the UTF-8 byte length, a true upper bound for the text because every text token covers at least one byte, with template tokens added on top. The gate also compares its count with the <code>usage.prompt_tokens<\/code> the model reports.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Compaction for long conversations<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Under budget, a request passes through byte for byte, as almost all traffic does. Over budget, the gate asks the compaction service, which is loopback-only, uses no GPU and has a 512 MB memory limit, to compact the transcript within a fixed time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The service archives every message to Postgres, deduplicated by content hash, so nothing is lost even when it&#8217;s left out of the prompt. That archive is sensitive data in its own right. In the lab it holds only test conversations on a loopback-only database, but a real deployment needs access controls and a retention policy from day one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It keeps every system message, the last 8 messages, the current question and every <strong>tool call<\/strong> with its result. A tool call is the model asking to run a function and getting a result back, and a result without its call is invalid input. Older material is carried forward as an <strong>extractive digest<\/strong>: the most relevant older messages, kept word for word and tagged with their archive hash, ranked by 0.6 embedding similarity plus 0.4 keyword overlap with <code>nomic-embed-text<\/code>. When a single message is too big, the service cuts inside it rather than dropping it, keeping the head and the last third and leaving a marker:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>[... 41203 characters dropped to fit the context budget \u2014 original archived as a1b2c3d4e5f6]<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The gate never trusts the result. It recounts, and checks message order, tool pairing, that every system message survived and that the current question is unchanged. Any failure, including the service being slow or down, falls back to the same <code>400<\/code>, so an unhealthy compaction service can&#8217;t break the gateway.<\/p>\n\n\n\n<div class=\"wp-block-group has-pale-cyan-blue-background-color has-background\" style=\"padding-top:28px;padding-right:32px;padding-bottom:28px;padding-left:32px;margin-top:32px;margin-bottom:32px\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-40de7cf3 wp-block-group-is-layout-constrained\">\n\n<p class=\"wp-block-paragraph\"><strong>Thinking about private AI for your organisation?<\/strong> We design, build and secure on-premises AI platforms for Australian organisations, with the guardrails this series describes built in from the start.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/catharsis.net.au\/local-ai\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=local-ai-series&amp;utm_content=part-04-mid\" target=\"_blank\" rel=\"noreferrer noopener\">Explore Private AI<\/a><\/div>\n\n<\/div>\n\n<\/div><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"challenges-we-hit\">Challenges we hit<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Budgets that did nothing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Our gate reads budgets from <code>model_info<\/code>, so a budget written under <code>litellm_params<\/code> did nothing, and three models went unchecked for a day. A pre-commit check now refuses any deployment that reserves memory without a declared budget, and an hourly guard checks the running gateway. Our first budget was also too close to the slot, 16,000 against the earlier 16,384-token window, leaving almost no room to answer. We cut it to 13,000 the same day.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">A summary that was always empty<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The first design had <code>gemma4<\/code> write a rolling summary of older turns. In our setup gemma4 reasons before it answers, and that hidden &quot;thinking&quot; counted against <code>max_tokens<\/code>, so the 1,000-token allowance ran out and the summary came back empty every time. A bigger cap wouldn&#8217;t fit the slot. That is why the digest is extractive: it needs no model call and can&#8217;t distort the original. A second defect hid the evidence. LiteLLM filters request metadata through an allowlist before any logger sees it, so compaction details never reached the traces until we moved them into an allowed field.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Rewriting line one cost 90 times the GPU time<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Our second soak showed compaction made every turn re-process the whole prompt.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\">\n<thead>\n<tr>\n<th>Four turns of one conversation<\/th>\n<th>Prompt tokens re-processed<\/th>\n<th>Prompt processing time<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Compacted<\/td>\n<td>13,431 \/ 13,342 \/ 13,416 \/ 13,241<\/td>\n<td>5.45 \/ 5.11 \/ 5.17 \/ 5.08 s<\/td>\n<\/tr>\n<tr>\n<td>Same conversation, under budget<\/td>\n<td>5,715, then 23 \/ 23 \/ 23<\/td>\n<td>1.99 s, then 56 \/ 57 \/ 57 ms<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Local inference reuses its prompt cache only while the start of the prompt stays the same. Our digest sat at message zero, ahead of the caller&#8217;s system prompt, and was re-ranked every turn, so consecutive prompts shared just 67 characters out of 61,000. We rolled back and added <strong>checkpoints<\/strong>. The compacted prefix is stored with the hashes of the messages it replaces and reused while they still match, and the order is now system prompt, digest, selected middle, recent tail. The trade-off is that relevance is judged when a checkpoint is made, so a message that becomes relevant later waits for the next one.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Token counts wrong by up to 322 times<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The gate first used <code>litellm.token_counter<\/code>, which falls back to OpenAI&#8217;s <code>cl100k_base<\/code> tokenizer for an Ollama model, not the tokenizer Qwen or Gemma use. Within three hours, four prompts over the 13,000 budget of the time reached Ollama, at 15,597, 14,550, 14,525 and 14,467 tokens, and 59 of 76 measured samples on two models were unsafe. We traced four causes.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li>Tool definitions were never counted, though Ollama writes the whole schema into the prompt. A short message with twelve tool definitions counted as 11 tokens and cost 3,540.<\/li>\n\n\n<li><code>cl100k_base<\/code> groups digits in threes, while Qwen and Gemma spend one token per digit. 4,000 digits counted as 1,343 and cost 4,010.<\/li>\n\n\n<li>Each message carries about 2 chat-template tokens the counter didn&#8217;t charge.<\/li>\n\n\n<li>With those fixed, the count is exact on Qwen (a median of 1.004 times the real count) but up to 20% light on Gemma, hence the margins.<\/li>\n\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">The old fallback, <code>len(text) \/\/ 4<\/code>, undercounted emoji by 9.9 times. The stock-analysis test client had the same habit in its own trimmer. Our first guess blamed tool schemas at &quot;about 7,000 tokens&quot;, but they measured 807, 1,104 and 865. On a failed transcript <code>len\/\/4<\/code> said 22,729 where the gate counted 63,437, 2.79 times too optimistic, because market data is mostly prices, volumes and dates. Characters per token simply isn&#8217;t a constant:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\">\n<thead>\n<tr>\n<th>Kind of text<\/th>\n<th>Characters per token<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Prose<\/td>\n<td>4.4\u20135.0<\/td>\n<\/tr>\n<tr>\n<td>Scraped HTML<\/td>\n<td>2.96<\/td>\n<\/tr>\n<tr>\n<td>Minified JSON<\/td>\n<td>2.59<\/td>\n<\/tr>\n<tr>\n<td>Log lines<\/td>\n<td>2.33<\/td>\n<\/tr>\n<tr>\n<td>Tool-call JSON<\/td>\n<td>2.1\u20132.2<\/td>\n<\/tr>\n<tr>\n<td>UUIDs<\/td>\n<td>1.58<\/td>\n<\/tr>\n<tr>\n<td>Price\/volume (OHLCV) tool output<\/td>\n<td>1.27\u20131.72<\/td>\n<\/tr>\n<tr>\n<td>Chinese<\/td>\n<td>1.18<\/td>\n<\/tr>\n<tr>\n<td>Emoji<\/td>\n<td>0.38<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The compaction service now prices each run of text by its kind. Embedding chunks are sized the same way, after a chunker that assumed 3.0 characters per token sent an 1,800-token chunk that arrived as 2,964 and was refused.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Agents calling the same tools again and again<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once the count was honest, analyses failed by hitting the agent framework&#8217;s recursion limit. An analyst called nine tools in one turn, the next turn was over budget, and the condenser replaced those results with a summary. Without the answers it had just received, the model called the same tools again. For a tool-calling agent, condensing the middle is the wrong approach, which is why the service now keeps every pairing and cuts inside large messages. If even short placeholders don&#8217;t fit, an &quot;already called&quot; list at the head of the digest tells the model not to repeat itself.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-result\">The result<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A 122-message, 73,812-token transcript that used to get a <code>400<\/code> was answered in 7.8 seconds from a 12,685-token prompt, and two facts we had planted deep in the dropped part both appeared in the answer. A reused checkpoint re-processes 17 to 20 tokens in about 60 ms instead of about 5 seconds. On the analyst pattern, compaction went from <code>kept 9 of 22<\/code> messages to <code>kept 22 of 22<\/code>, turning a 53,070-token request into 18,240 tokens with a correct answer. A loop of 40 <code>get_indicators<\/code> pairs compacted from 89,996 to 22,074 tokens, and the model wrote its report without another call. Pricing text by kind lifted budget use on digit-heavy data from about 52% to 86%, never over.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With 32k per request and cutting inside messages, we retired the client&#8217;s own trimmer, since two condensers in a row only hide what the second one did. The first run afterwards, on an uncached test ticker, finished in 263 seconds with no truncation, and two of its calls were above the old 13,000-token budget.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The costs: on 527 recorded prompt sizes, the honest count refuses 1.71 percentage points more traffic on Qwen, most of it already over budget in reality, and Gemma&#8217;s 25% margin refuses some prose that would have fitted. A compacting turn adds a few seconds, and a client waits up to 4 seconds for its <code>400<\/code> if the service is slow. There is also more to run: a service, an archive, a vector cache and a checkpoint store.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"do-it-yourself-an-admission-check-in-litellm\">Do it yourself: an admission check in LiteLLM<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">What you need: a LiteLLM proxy in front of Ollama, Python with the <code>transformers<\/code> package, and access to each model&#8217;s tokenizer on Hugging Face (some, such as Gemma, require you to accept a licence and use an access token). Allow half a day.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n\n<li>\n<p><strong>Declare a budget per model.<\/strong> In <code>config.yaml<\/code>, give each entry in <code>model_list<\/code> a budget under <code>model_info<\/code>, set below the model&#8217;s <code>num_ctx<\/code> so the answer fits.<\/p>\n<pre><code class=\"language-yaml\">model_list:\n  - model_name: qwen3-coder\n    litellm_params:\n      model: ollama_chat\/qwen3-coder-32k\n      api_base: http:\/\/127.0.0.1:11434\n    model_info:\n      max_input_tokens: 26000\n<\/code><\/pre>\n<p>Check: <code>GET \/model\/info<\/code> on the proxy returns the budget for every model.<\/p>\n<\/li>\n\n\n<li>\n<p><strong>Write the hook<\/strong> as <code>context_guard.py<\/code>. It must return <code>data<\/code> to let a request through and raise an <code>HTTPException<\/code> to refuse it. Here the budgets are a dictionary; in a real deployment, read them from <code>\/model\/info<\/code>.<\/p>\n<pre><code class=\"language-python\">from fastapi import HTTPException\nfrom litellm.integrations.custom_logger import CustomLogger\n\nBUDGETS = {&quot;qwen3-coder&quot;: 26000}\n\nclass ContextGuard(CustomLogger):\n    async def async_pre_call_hook(self, user_api_key_dict, cache, data, call_type):\n        n = count_tokens(data)  # step 4: messages, tools and template overhead\n        budget = BUDGETS.get(data.get(&quot;model&quot;), 13000)\n        if n &gt; budget:\n            raise HTTPException(status_code=400, detail={&quot;error&quot;: {\n                &quot;code&quot;: &quot;context_length_exceeded&quot;,\n                &quot;message&quot;: f&quot;{n} tokens is over the {budget}-token budget&quot;}})\n        return data\n\nguard_instance = ContextGuard()\n<\/code><\/pre>\n<\/li>\n\n\n<li>\n<p><strong>Register it.<\/strong> Put the module next to <code>config.yaml<\/code> and list its instance, not the class, under <code>litellm_settings<\/code>.<\/p>\n<pre><code class=\"language-yaml\">litellm_settings:\n  callbacks:\n    - context_guard.guard_instance\n<\/code><\/pre>\n<p>Check: the proxy starts cleanly. Start it once with <code>--detailed_debug<\/code> to see the callback registered, or simply run step 7; a refusal proves the hook is live.<\/p>\n<\/li>\n\n\n<li>\n<p><strong>Count with the model&#8217;s own tokenizer.<\/strong> Render the conversation with the tokenizer&#8217;s chat template, including the tools, and count the result.<\/p>\n<pre><code class=\"language-python\">from transformers import AutoTokenizer\ntok = AutoTokenizer.from_pretrained(&quot;Qwen\/Qwen3-Coder-30B-A3B-Instruct&quot;)\nids = tok.apply_chat_template(data[&quot;messages&quot;], tools=data.get(&quot;tools&quot;),\n                              add_generation_prompt=True, tokenize=True)\nn = len(ids)\n<\/code><\/pre>\n<p>The Hugging Face template isn&#8217;t identical to the one Ollama runs, so treat this as close, not exact, and calibrate in step 8. Check: one short message with a dozen tool definitions counts in the thousands, not in the tens.<\/p>\n<\/li>\n\n\n<li>\n<p><strong>Add a margin and a safe fallback.<\/strong> Multiply the count by a measured margin per model, and fall back to the UTF-8 byte length if the tokenizer fails. Check: a string of 4,000 digits counts at about 4,000.<\/p>\n<\/li>\n\n\n<li>\n<p><strong>Cover embeddings.<\/strong> <code>call_type<\/code> includes embeddings, so the same hook can check each item in the request&#8217;s <code>input<\/code>. Callers that use Ollama&#8217;s own <code>\/api\/embed<\/code> can also send <code>&quot;truncate&quot;: false<\/code> to get an error instead of a clipped input. Check: an oversized input gets a <code>400<\/code>, not a <code>200<\/code> with a clipped <code>prompt_tokens<\/code>.<\/p>\n<\/li>\n\n\n<li>\n<p><strong>Test the refusal.<\/strong> Send a prompt well over budget. Check: you get <code>400 context_length_exceeded<\/code> within milliseconds, and Ollama&#8217;s log shows no <code>truncating input prompt<\/code>.<\/p>\n<\/li>\n\n\n<li>\n<p><strong>Keep checking against reality.<\/strong> Log the ratio of your count to the <code>usage.prompt_tokens<\/code> each response reports. Check: the ratio sits at or just above 1.0 for every model.<\/p>\n<\/li>\n\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Compaction is a bigger build. Start with the refusal, then add a compaction service that keeps system messages, the recent tail and every tool call with its result, as described above.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"key-takeaways\">Key takeaways<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n\n<li>A model&#8217;s context window is your budget. Enforce it at the gateway, below the slot, because silent truncation is worse than a clear error.<\/li>\n\n\n<li>Count the way the model counts. Tool definitions, digits and chat templates are where generic counters went wrong, by up to 322 times.<\/li>\n\n\n<li>Never estimate with <code>len \/\/ 4<\/code>. Characters per token ran from 0.38 to 5.0.<\/li>\n\n\n<li>Keep the start of the prompt stable, or local inference re-processes everything every turn.<\/li>\n\n\n<li>For agents, keep every tool call with its result and cut inside large outputs instead.<\/li>\n\n\n<li>Soak-test any change to the prompt prefix and compare prompt-processing time, not just correctness.<\/li>\n\n<\/ul>\n\n\n\n<div class=\"wp-block-group has-light-green-cyan-background-color has-background\" style=\"padding-top:28px;padding-right:32px;padding-bottom:28px;padding-left:32px;margin-top:40px;margin-bottom:32px\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-40de7cf3 wp-block-group-is-layout-constrained\">\n\n<h2 class=\"wp-block-heading\">Planning private AI?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Catharsis Cyber Security is a Sydney cyber security company. We design, build and secure private AI platforms like the one in this lab, and help you decide where local or cloud AI fits.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/catharsis.net.au\/contact-us\/?type=contact&amp;service=local-ai&amp;utm_source=blog&amp;utm_medium=article&amp;utm_campaign=local-ai-series&amp;utm_content=part-04-contact\" target=\"_blank\" rel=\"noreferrer noopener\">Talk to us about AI<\/a><\/div>\n\n\n<div class=\"wp-block-button is-style-outline is-style-outline--1\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/catharsis.net.au\/secure-ai\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=local-ai-series&amp;utm_content=part-04-end\" target=\"_blank\" rel=\"noreferrer noopener\">Explore Secure AI<\/a><\/div>\n\n<\/div>\n\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-group has-background\" style=\"background-color:#f4f6f8;padding-top:28px;padding-right:32px;padding-bottom:28px;padding-left:32px\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-container-core-group-is-layout-40de7cf3 wp-block-group-is-layout-constrained\">\n\n<p class=\"wp-block-paragraph\"><strong>Keep reading the series<\/strong><\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n\n<div class=\"wp-block-button is-style-outline is-style-outline--2\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/catharsis.net.au\/blog\/local-ai-server-freezes-unified-memory\/\" target=\"_blank\" rel=\"noreferrer noopener\">\u2190 Part 3<\/a><\/div>\n\n\n<div class=\"wp-block-button is-style-outline is-style-outline--3\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/catharsis.net.au\/blog\/running-local-ai-in-production-series-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Series guide<\/a><\/div>\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/catharsis.net.au\/blog\/smart-routing-single-gpu-local-llm\/\" target=\"_blank\" rel=\"noreferrer noopener\">Next: Part 5 \u2192<\/a><\/div>\n\n<\/div>\n\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>On a local AI server, a prompt too big for the model gets silently truncated, and raising the window for every parallel slot is what froze our lab server. We built admission control and gateway-side compaction, then found our token counts were out by up to 322 times.<\/p>\n","protected":false},"author":2,"featured_media":1094,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[69],"tags":[87,96,83,95,91,81,75,80,94,97],"class_list":["post-1095","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","tag-admission-control","tag-ai-agents","tag-ai-lab","tag-compaction","tag-context-window","tag-litellm","tag-local-llm","tag-ollama","tag-token-counting","tag-tool-calls"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11) - Catharsis | Blog<\/title>\n<meta name=\"description\" content=\"What happens when a prompt is too big for a local LLM? It is cut silently. How a lab gateway refuses, compacts and counts tokens the way the model does.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11) - Catharsis | Blog\" \/>\n<meta property=\"og:description\" content=\"What happens when a prompt is too big for a local LLM? It is cut silently. How a lab gateway refuses, compacts and counts tokens the way the model does.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/\" \/>\n<meta property=\"og:site_name\" content=\"Catharsis | Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-06T23:30:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-11-03T09:27:55+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2026\/11\/local-ai-series-part-04.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"720\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Sajal Jain\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Sajal Jain\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"18 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/\"},\"author\":{\"name\":\"Sajal Jain\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#\\\/schema\\\/person\\\/f0826efb92a7aaf8d46b24715990e099\"},\"headline\":\"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11)\",\"datePublished\":\"2026-09-06T23:30:00+00:00\",\"dateModified\":\"2026-11-03T09:27:55+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/\"},\"wordCount\":3333,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/local-ai-series-part-04.jpg\",\"keywords\":[\"admission control\",\"AI agents\",\"AI lab\",\"compaction\",\"context window\",\"LiteLLM\",\"local LLM\",\"Ollama\",\"token counting\",\"tool calls\"],\"articleSection\":[\"Artificial Intelligence\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/\",\"url\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/\",\"name\":\"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11) - Catharsis | Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/local-ai-series-part-04.jpg\",\"datePublished\":\"2026-09-06T23:30:00+00:00\",\"dateModified\":\"2026-11-03T09:27:55+00:00\",\"description\":\"What happens when a prompt is too big for a local LLM? It is cut silently. How a lab gateway refuses, compacts and counts tokens the way the model does.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#primaryimage\",\"url\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/local-ai-series-part-04.jpg\",\"contentUrl\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/local-ai-series-part-04.jpg\",\"width\":1280,\"height\":720,\"caption\":\"Close-up of chips on a circuit board\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/local-llm-context-budget\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/\",\"name\":\"Catharsis | Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#organization\",\"name\":\"Catharsis | Blog\",\"url\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/catharsis_au.jpg\",\"contentUrl\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/catharsis_au.jpg\",\"width\":215,\"height\":60,\"caption\":\"Catharsis | Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/#\\\/schema\\\/person\\\/f0826efb92a7aaf8d46b24715990e099\",\"name\":\"Sajal Jain\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/Sajal-copy-96x96.jpg\",\"url\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/Sajal-copy-96x96.jpg\",\"contentUrl\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/Sajal-copy-96x96.jpg\",\"caption\":\"Sajal Jain\"},\"description\":\"Cyber Security Enthusiast | OSCP | CEH | CHFI | CNSS | Master of IT Security &amp; Master of IT Management\",\"url\":\"https:\\\/\\\/catharsis.net.au\\\/blog\\\/author\\\/sajal-jain\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11) - Catharsis | Blog","description":"What happens when a prompt is too big for a local LLM? It is cut silently. How a lab gateway refuses, compacts and counts tokens the way the model does.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/","og_locale":"en_US","og_type":"article","og_title":"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11) - Catharsis | Blog","og_description":"What happens when a prompt is too big for a local LLM? It is cut silently. How a lab gateway refuses, compacts and counts tokens the way the model does.","og_url":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/","og_site_name":"Catharsis | Blog","article_published_time":"2026-09-06T23:30:00+00:00","article_modified_time":"2026-11-03T09:27:55+00:00","og_image":[{"width":1280,"height":720,"url":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2026\/11\/local-ai-series-part-04.jpg","type":"image\/jpeg"}],"author":"Sajal Jain","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Sajal Jain","Est. reading time":"18 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#article","isPartOf":{"@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/"},"author":{"name":"Sajal Jain","@id":"https:\/\/catharsis.net.au\/blog\/#\/schema\/person\/f0826efb92a7aaf8d46b24715990e099"},"headline":"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11)","datePublished":"2026-09-06T23:30:00+00:00","dateModified":"2026-11-03T09:27:55+00:00","mainEntityOfPage":{"@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/"},"wordCount":3333,"commentCount":0,"publisher":{"@id":"https:\/\/catharsis.net.au\/blog\/#organization"},"image":{"@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#primaryimage"},"thumbnailUrl":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2026\/11\/local-ai-series-part-04.jpg","keywords":["admission control","AI agents","AI lab","compaction","context window","LiteLLM","local LLM","Ollama","token counting","tool calls"],"articleSection":["Artificial Intelligence"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/","url":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/","name":"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11) - Catharsis | Blog","isPartOf":{"@id":"https:\/\/catharsis.net.au\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#primaryimage"},"image":{"@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#primaryimage"},"thumbnailUrl":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2026\/11\/local-ai-series-part-04.jpg","datePublished":"2026-09-06T23:30:00+00:00","dateModified":"2026-11-03T09:27:55+00:00","description":"What happens when a prompt is too big for a local LLM? It is cut silently. How a lab gateway refuses, compacts and counts tokens the way the model does.","breadcrumb":{"@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#primaryimage","url":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2026\/11\/local-ai-series-part-04.jpg","contentUrl":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2026\/11\/local-ai-series-part-04.jpg","width":1280,"height":720,"caption":"Close-up of chips on a circuit board"},{"@type":"BreadcrumbList","@id":"https:\/\/catharsis.net.au\/blog\/local-llm-context-budget\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/catharsis.net.au\/blog\/"},{"@type":"ListItem","position":2,"name":"What Happens When a Prompt Is Too Big for a Local LLM? (Part 4 of 11)"}]},{"@type":"WebSite","@id":"https:\/\/catharsis.net.au\/blog\/#website","url":"https:\/\/catharsis.net.au\/blog\/","name":"Catharsis | Blog","description":"","publisher":{"@id":"https:\/\/catharsis.net.au\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/catharsis.net.au\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/catharsis.net.au\/blog\/#organization","name":"Catharsis | Blog","url":"https:\/\/catharsis.net.au\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/catharsis.net.au\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2019\/01\/catharsis_au.jpg","contentUrl":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2019\/01\/catharsis_au.jpg","width":215,"height":60,"caption":"Catharsis | Blog"},"image":{"@id":"https:\/\/catharsis.net.au\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/catharsis.net.au\/blog\/#\/schema\/person\/f0826efb92a7aaf8d46b24715990e099","name":"Sajal Jain","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2019\/01\/Sajal-copy-96x96.jpg","url":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2019\/01\/Sajal-copy-96x96.jpg","contentUrl":"https:\/\/catharsis.net.au\/blog\/wp-content\/uploads\/2019\/01\/Sajal-copy-96x96.jpg","caption":"Sajal Jain"},"description":"Cyber Security Enthusiast | OSCP | CEH | CHFI | CNSS | Master of IT Security &amp; Master of IT Management","url":"https:\/\/catharsis.net.au\/blog\/author\/sajal-jain\/"}]}},"_links":{"self":[{"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/posts\/1095","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/comments?post=1095"}],"version-history":[{"count":3,"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/posts\/1095\/revisions"}],"predecessor-version":[{"id":1150,"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/posts\/1095\/revisions\/1150"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/media\/1094"}],"wp:attachment":[{"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/media?parent=1095"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/categories?post=1095"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/catharsis.net.au\/blog\/wp-json\/wp\/v2\/tags?post=1095"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}