<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Talha Anwar</title>
    <description>The latest articles on DEV Community by Talha Anwar (@ailegend).</description>
    <link>https://dev.to/ailegend</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4067988%2F23197b0f-0d13-439f-8ceb-931a658a3403.png</url>
      <title>DEV Community: Talha Anwar</title>
      <link>https://dev.to/ailegend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ailegend"/>
    <language>en</language>
    <item>
      <title>LangSmith Just Shipped an LLM Gateway. Here's the 3-Year Mess That Made Me Care</title>
      <dc:creator>Talha Anwar</dc:creator>
      <pubDate>Sat, 08 Aug 2026 13:33:14 +0000</pubDate>
      <link>https://dev.to/ailegend/langsmith-just-shipped-an-llm-gateway-heres-the-3-year-mess-that-made-me-care-1bfh</link>
      <guid>https://dev.to/ailegend/langsmith-just-shipped-an-llm-gateway-heres-the-3-year-mess-that-made-me-care-1bfh</guid>
      <description>&lt;p&gt;Just found that langsmith has launched gateway feature and I decided to share my experience. It is about how messy real LLM tooling decisions actually are, and why a small feature announcement from LangSmith made me sit up this month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it all started
&lt;/h2&gt;

&lt;p&gt;In August 2023, I onboarded my first RAG (retrieval-augmented generation — you fetch relevant documents first, then hand them to the model as context) chatbot client as an AI lead. Back then the models we had access to were much weaker and much less reliable than what you get today. Hallucinations were common, context windows were small, and you spent half your time just making the model behave.&lt;/p&gt;

&lt;p&gt;The setup was simple, though. One client, one model, one hardcoded prompt in the code. No observability tool. No gateway. It just worked because there was nothing to break.&lt;/p&gt;

&lt;h2&gt;
  
  
  The step that looked solved
&lt;/h2&gt;

&lt;p&gt;Then we got client number two. Then three. The obvious move was to copy the same folder, swap the API key, and ship it. For a while, that felt like scaling.&lt;/p&gt;

&lt;p&gt;It was not. Our prompts lived scattered across different codebases. Our logs sat in our own Postgres database, one table per project, with no shared view across clients. When something went wrong for one client, we had no fast way to tell if it was that client's prompt, a model change, or something else entirely.&lt;/p&gt;

&lt;p&gt;That is the real friction nobody warns you about. A broken prompt does not throw an error. It just quietly returns worse answers, and you might not notice for days — by which time a client has already noticed for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision that forced
&lt;/h2&gt;

&lt;p&gt;That friction split into two separate problems we had to solve on purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Where do we store and version prompts, and see what is actually happening across every client's traces (a trace is the full record of one request — every step, every token, every millisecond)?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How do we talk to multiple model providers through one shape, so a provider outage does not become our outage?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first is an observability or "LLM ops" platform. The second is a gateway. Here is the order we actually went through both, mistakes included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round one: LangSmith, and giving up on it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.langchain.com/langsmith" rel="noopener noreferrer"&gt;LangSmith&lt;/a&gt; is LangChain's platform for tracing LLM calls, storing and versioning prompts, and running evaluations. We picked it up early because it was the obvious default in the LangChain ecosystem, and we were already leaning on some LangChain pieces.&lt;/p&gt;

&lt;p&gt;We stuck with it for a few months before we dropped it. It kept breaking in ways that made us stop trusting the data it showed us — traces that did not line up with what we already had in our own Postgres logs. When your monitoring tool is less reliable than the thing it is monitoring, you stop using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round two: the platform Anthropic later swallowed whole
&lt;/h2&gt;

&lt;p&gt;So we moved to &lt;a href="https://humanloop.com/" rel="noopener noreferrer"&gt;Humanloop&lt;/a&gt;, a platform built for the same prompt-management-and-evaluation job. It worked well enough for a good stretch.&lt;/p&gt;

&lt;p&gt;Then, in August 2025, &lt;a href="https://techcrunch.com/2025/08/13/anthropic-nabs-humanloop-team-as-competition-for-enterprise-ai-talent-heats-up" rel="noopener noreferrer"&gt;Anthropic acqui-hired Humanloop's founders and most of its team&lt;/a&gt; — no product, no IP, just the people. Humanloop as a platform sunset shortly after. If you have never seen an acqui-hire before: a bigger company hires the team, not the company, and the product itself just stops being maintained. We had to move a second time, through no fault of our own engineering choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round three: back to LangSmith, warts and all
&lt;/h2&gt;

&lt;p&gt;By then LangSmith had matured, and honestly, we did not have many other doors left to knock on. We are still on it today. It is not the platform I would design from scratch — there are features we need that are still missing — but it is stable enough now, and it gives us the cross-client visibility we were missing back in 2023.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meanwhile, the gateway problem: why we picked LiteLLM
&lt;/h2&gt;

&lt;p&gt;While all that platform-hopping was happening, we separately needed a gateway (a layer that sits between your app and every model provider, so your code talks to one consistent API instead of five different ones). We landed on &lt;a href="https://docs.litellm.ai/" rel="noopener noreferrer"&gt;LiteLLM&lt;/a&gt;, which we host ourselves on our own server.&lt;/p&gt;

&lt;p&gt;LiteLLM is not perfect. We know about specific bugs in it, and we live with them, because the thing it does right is the thing we cannot do without: &lt;a href="https://docs.litellm.ai/docs/proxy/reliability" rel="noopener noreferrer"&gt;automatic fallback&lt;/a&gt; to another model or provider when the primary one errors out or gets rate-limited. That single feature is probably the most-used piece of LiteLLM in our whole stack. When a provider has a bad day, our clients never know.&lt;/p&gt;

&lt;h2&gt;
  
  
  This month: LangSmith adds its own Gateway
&lt;/h2&gt;

&lt;p&gt;Then this month I noticed LangSmith has its own &lt;a href="https://www.langchain.com/langsmith/llm-gateway" rel="noopener noreferrer"&gt;LLM Gateway&lt;/a&gt; — &lt;a href="https://www.langchain.com/blog/introducing-llm-gateway" rel="noopener noreferrer"&gt;announced in private beta on May 13, 2026, and opened to public beta on July 30, 2026&lt;/a&gt;. My first reaction was genuinely good: one platform for traces, prompts, and provider routing instead of two separate systems to keep in sync.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwp1jzgsa652kgqeik9o8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwp1jzgsa652kgqeik9o8.png" alt="Langsmith introduces gateway functionality" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I went in assuming the one feature we could not give up — fallback — would still be missing, because the original May announcement listed model fallback under "where this is going," as a future plan rather than a shipped feature. I was wrong about that. By the July public beta, &lt;a href="https://www.langchain.com/langsmith/llm-gateway" rel="noopener noreferrer"&gt;LangSmith Gateway already ships configurable model fallbacks&lt;/a&gt;, chained across models, providers, and hosts, triggered by rate limits or provider errors. That is genuinely good news, since fallback is the one LiteLLM feature we would have refused to give up.&lt;/p&gt;

&lt;p&gt;But here is the part I still cannot skip: &lt;strong&gt;latency.&lt;/strong&gt; Our LiteLLM instance is self-hosted, on our own server. A request goes from our app to a local process and out to the provider — no extra hop over the public internet. LangSmith Gateway is a hosted, external service. Routing every completion through it adds a real network round trip we do not currently pay.&lt;/p&gt;

&lt;p&gt;A fallback that reads well on a feature page has not been through a real outage on our own traffic yet. Before we retire LiteLLM, I want to see two numbers with our own eyes: the added latency per request, and how the fallback behaves the first time a provider actually goes down on us, not in a demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell you, if you were the one making this call
&lt;/h2&gt;

&lt;p&gt;Do not trust a feature list over battle scars, even when the feature list is telling the truth. Test the exact thing you depend on most, under the exact load you actually run, before you let go of the tool that is already working.&lt;/p&gt;

&lt;p&gt;Three years, four tools, one lesson that keeps repeating: the tool that wins is not the one with the longest feature list. It is the one that is still standing after the outage nobody planned for.&lt;/p&gt;

&lt;p&gt;So tell me your version of this. Has anyone already pushed LangSmith Gateway through a real provider outage or a hard rate limit on real production traffic? Or did you go through your own round of tool-hopping — what made you leave a platform, and what finally made one stick? I would genuinely like to hear it before we run our own test.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>discuss</category>
      <category>llm</category>
    </item>
    <item>
      <title>Every Check Said Our GA4 Analytics Worked. It Had Never Recorded a Visit</title>
      <dc:creator>Talha Anwar</dc:creator>
      <pubDate>Sat, 08 Aug 2026 10:06:48 +0000</pubDate>
      <link>https://dev.to/ailegend/every-check-said-our-ga4-analytics-worked-it-had-never-recorded-a-visit-1cn0</link>
      <guid>https://dev.to/ailegend/every-check-said-our-ga4-analytics-worked-it-had-never-recorded-a-visit-1cn0</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Adding analytics to a website is the most boring task in web development. Google hands you eight lines, you paste them above &lt;code&gt;&amp;lt;/head&amp;gt;&lt;/code&gt;, and traffic shows up in the dashboard about a minute later.&lt;/p&gt;

&lt;p&gt;On a real app you tidy it up a little. The measurement ID moves into an environment variable, the snippet becomes a module, and you add a cookie banner with &lt;a href="https://developers.google.com/tag-platform/security/guides/consent" rel="noopener noreferrer"&gt;Consent Mode&lt;/a&gt; so nothing is tracked before a visitor agrees. Still a small job, and it still looks finished when the banner renders.&lt;/p&gt;

&lt;p&gt;Here's the part nobody warns you about: &lt;strong&gt;every layer you just added can fail without saying a word.&lt;/strong&gt; A build tool can drop the env var. A queue can accept commands it will never run. A CI secret can point at a property nobody is watching. None of them throws. None of them logs. The site loads fine, the banner works, and the dashboard stays at zero.&lt;/p&gt;

&lt;p&gt;So the only check that means anything is the last one — did a hit actually arrive? This is the story of the three silent failures I had to clear before the answer was yes, and the second one nearly had me file a bug report against Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I work on &lt;a href="https://acruxcore.com" rel="noopener noreferrer"&gt;AcruxCore&lt;/a&gt;, an LLM ops platform. Two sites: the marketing site and dashboard (&lt;code&gt;apps/web&lt;/code&gt;, React + Vite, deployed in Docker), and the docs site (&lt;code&gt;apps/docs&lt;/code&gt;, Docusaurus, deployed to Cloudflare Pages).&lt;/p&gt;

&lt;p&gt;I shipped Google Analytics 4 to both in one PR. Consent Mode v2 with &lt;code&gt;analytics_storage&lt;/code&gt; denied by default, a cookie banner, consent stored as a first-party cookie shared across subdomains.&lt;/p&gt;

&lt;p&gt;I tested it properly before merging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Playwright against a real dev server — accept, decline, reload, reopen from the footer&lt;/li&gt;
&lt;li&gt;built the bundle with the env var set and grepped the output for the measurement ID&lt;/li&gt;
&lt;li&gt;built it again with the var unset and confirmed the tag never loads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All green. I merged it, rebuilt the production image, and opened Google's tag inspector on the live site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tag not detected.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 1 — the tag was never in the bundle
&lt;/h2&gt;

&lt;p&gt;The cookie banner rendered on production, so the new frontend had definitely deployed. That's what made this confusing: the same PR shipped both, and only half of it arrived.&lt;/p&gt;

&lt;p&gt;I SSH'd into the VPS and worked backward from the artifact rather than forward from the config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# search the actually-served bundle for Google's script host&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;acruxcore-web &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rc&lt;/span&gt; googletagmanager /usr/share/nginx/html/assets/
&lt;span class="c"&gt;# → 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero. Not a wrong ID, not a consent problem — the tag was never compiled in. A rebuild with &lt;code&gt;docker compose build web&lt;/code&gt; still gave zero, and a full &lt;code&gt;docker build --no-cache&lt;/code&gt; reproduced the same empty result.&lt;/p&gt;

&lt;p&gt;At that point I'd checked the Docker &lt;code&gt;ARG&lt;/code&gt;/&lt;code&gt;ENV&lt;/code&gt; lines, the &lt;code&gt;args:&lt;/code&gt; block in &lt;code&gt;docker-compose.yml&lt;/code&gt;, and the &lt;code&gt;define&lt;/code&gt; in &lt;code&gt;vite.config.ts&lt;/code&gt;. All three were correct. So I stopped reading config and ran the build one layer down, inside the same built image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# bypass the orchestrator, call the bundler directly&lt;/span&gt;
&lt;span class="nv"&gt;GA4_MEASUREMENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;G-XXXXXXXXXX npx vite build
&lt;span class="c"&gt;# → googletagmanager IS in the output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vite build&lt;/code&gt; produced the tag. &lt;code&gt;turbo run build&lt;/code&gt; did not. Same image, same env, same everything else. The bug wasn't in Docker, Compose or Vite — it was in the layer between them.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Turborepo was doing
&lt;/h3&gt;

&lt;p&gt;Our Dockerfile builds through the monorepo task runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;npx turbo run build &lt;span class="nt"&gt;--filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@acruxcore/web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://turborepo.com/docs/reference/configuration#envmode" rel="noopener noreferrer"&gt;Turborepo's default &lt;code&gt;envMode&lt;/code&gt; is &lt;code&gt;strict&lt;/code&gt;&lt;/a&gt;. Strict means a task's process starts with a &lt;strong&gt;filtered&lt;/strong&gt; environment. It sees only the variables you declared in that task's &lt;code&gt;env&lt;/code&gt; config, plus any auto-inferred from a recognized framework's naming convention. For Vite, that's anything prefixed &lt;code&gt;VITE_&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our variable is called &lt;code&gt;GA4_MEASUREMENT_ID&lt;/code&gt;. It is deliberately &lt;em&gt;not&lt;/em&gt; &lt;code&gt;VITE_&lt;/code&gt;-prefixed, because &lt;code&gt;vite.config.ts&lt;/code&gt; remaps it at build time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;import.meta.env.VITE_GA4_MEASUREMENT_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GA4_MEASUREMENT_ID&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;?? ''&lt;/code&gt; is the whole tragedy in two characters. Turbo stripped &lt;code&gt;GA4_MEASUREMENT_ID&lt;/code&gt; &lt;strong&gt;before Vite's config file ever ran&lt;/strong&gt;, so &lt;code&gt;process.env.GA4_MEASUREMENT_ID&lt;/code&gt; was &lt;code&gt;undefined&lt;/code&gt;, the fallback kicked in, and the bundle compiled a perfectly valid empty string. Analytics reads the ID, finds &lt;code&gt;''&lt;/code&gt;, and correctly decides not to load. No error, because nothing went wrong — every layer did exactly what it was told.&lt;/p&gt;

&lt;p&gt;Docker and Compose passing the variable in correctly made no difference. Turbo removed it after they handed it over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx turbo run build &lt;span class="nt"&gt;--dry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json &lt;span class="nt"&gt;--filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@acruxcore/web &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.tasks[0].envMode, .tasks[0].environmentVariables.specified.env'&lt;/span&gt;
&lt;span class="c"&gt;# → "strict"&lt;/span&gt;
&lt;span class="c"&gt;# → []      ← nothing declared, so nothing gets through&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is one line in &lt;code&gt;turbo.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; "build": {
   "dependsOn": ["^build"],
   "outputs": ["dist/**", "build/**", ".docusaurus/**"],
&lt;span class="gi"&gt;+  "env": ["SENTRY_WEB_DSN", "GA4_MEASUREMENT_ID"]
&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Mirror file: &lt;a href="https://github.com/AcruxCore/AcruxCore/blob/main/turbo.json" rel="noopener noreferrer"&gt;&lt;code&gt;turbo.json&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The part that actually scared me
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GA4_MEASUREMENT_ID&lt;/code&gt; wasn't alone in that &lt;code&gt;env&lt;/code&gt; array. &lt;code&gt;SENTRY_WEB_DSN&lt;/code&gt; was there too, and I only added it because it has the same shape — a non-&lt;code&gt;VITE_&lt;/code&gt; name remapped in the same &lt;code&gt;define&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;Then I reverted the fix and re-ran the build with &lt;code&gt;SENTRY_WEB_DSN&lt;/code&gt; set, the same way I'd reproduced the GA4 failure. Identical result: stripped, empty string, no SDK.&lt;/p&gt;

&lt;p&gt;Sentry had been wired into the web app six days earlier. In that window, the browser SDK on the deployed site had almost certainly never sent a single error report.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And nothing would ever have told me.&lt;/strong&gt; A quiet analytics dashboard is at least ambiguous — maybe nobody visited. A quiet error monitor looks like &lt;em&gt;good news&lt;/em&gt;. That is the failure mode I keep thinking about: the tool whose whole job is telling you when something is wrong can be the one thing that's broken, and its silence reads as success.&lt;/p&gt;

&lt;p&gt;What wasn't affected. The docs site builds in CI with &lt;code&gt;npm run build -w @acruxcore/docs&lt;/code&gt; — never through Turbo — so it picked up the measurement ID on the first deploy. Half the platform worked from day one, which is exactly why I initially suspected something specific to the marketing site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 2 — the tag loaded, the queue filled, nothing was sent
&lt;/h2&gt;

&lt;p&gt;New image deployed. &lt;code&gt;googletagmanager&lt;/code&gt; now in the bundle, script loading in the network tab, cookie banner working, consent granted.&lt;/p&gt;

&lt;p&gt;GA4 Realtime: &lt;strong&gt;zero users.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I went through the browser console expecting an obvious break, and instead found every single signal healthy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;https://www.googletagmanager.com/gtag/js?id=…&lt;/code&gt; script loads with a 200&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;window.google_tag_manager&lt;/code&gt; is defined&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;window.dataLayer.push !== Array.prototype.push&lt;/code&gt; — gtag.js has replaced the queue's push, so it &lt;em&gt;is&lt;/em&gt; watching&lt;/li&gt;
&lt;li&gt;the queue fills up with my &lt;code&gt;consent&lt;/code&gt;, &lt;code&gt;js&lt;/code&gt; and &lt;code&gt;config&lt;/code&gt; commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And no request to &lt;code&gt;/g/collect&lt;/code&gt;. No &lt;code&gt;_ga&lt;/code&gt; cookie. Nothing dispatched, ever.&lt;/p&gt;

&lt;p&gt;I also ran GA4's own &lt;strong&gt;"Test your website"&lt;/strong&gt; check from the admin panel. It went green — which I now know only means the tag script is present on the page. It never checks whether a hit arrives.&lt;/p&gt;

&lt;h3&gt;
  
  
  The wrong turn
&lt;/h3&gt;

&lt;p&gt;Everything on my side was correct, so I started building the case that the problem was Google's.&lt;/p&gt;

&lt;p&gt;I set up a second, completely unrelated GA4 property and pointed the site at it. Identical zero-dispatch behaviour. That felt conclusive: not my account, not my property, not my tag config.&lt;/p&gt;

&lt;p&gt;I got as far as writing a standalone &lt;code&gt;ga4-zero-hits-repro.html&lt;/code&gt; to hand over, drafting the issue report, and opening a PR to track the escalation. The report was finished and queued to post.&lt;/p&gt;

&lt;h3&gt;
  
  
  The moment it broke
&lt;/h3&gt;

&lt;p&gt;Before posting I re-read my own repro page, and the thing I'd been ignoring finally registered. I had &lt;strong&gt;hand-written that page from Google's documentation&lt;/strong&gt; rather than copying the code my product actually runs.&lt;/p&gt;

&lt;p&gt;Which means it contained Google's canonical stub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And my product contained mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those look equivalent. TypeScript is happy, ESLint prefers the second one, and every reviewer including me read straight past it. My "repro of the bug" had quietly swapped out the one line that &lt;em&gt;was&lt;/em&gt; the bug.&lt;/p&gt;

&lt;p&gt;So I stopped reasoning and isolated the single variable. Two pages served from the same local origin, same measurement ID, same Consent Mode sequence, differing only in the push form:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;stub&lt;/th&gt;
&lt;th&gt;hits to &lt;code&gt;/g/collect&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;_ga&lt;/code&gt; cookie&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;push(args)&lt;/code&gt; — what shipped&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;not set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;push(arguments)&lt;/code&gt; — canonical&lt;/td&gt;
&lt;td&gt;2 (&lt;code&gt;page_view&lt;/code&gt;, &lt;code&gt;scroll&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;set&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One line. Everything else identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;arguments&lt;/code&gt; is load-bearing
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;gtag.js&lt;/code&gt; replaces &lt;code&gt;dataLayer.push&lt;/code&gt; with its own processor. That queue carries two different kinds of thing, and the processor has to tell them apart:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;commands&lt;/strong&gt; — &lt;code&gt;gtag('config', 'G-…')&lt;/code&gt;, &lt;code&gt;gtag('consent', 'update', {…})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;data-layer objects&lt;/strong&gt; — ordinary values other tags push in for their own use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It tells them apart by checking whether the pushed value is a real &lt;code&gt;arguments&lt;/code&gt; object. A rest parameter gives you a plain &lt;code&gt;Array&lt;/code&gt; instead, which fails that check — so the value gets filed away as data and no command ever runs.&lt;/p&gt;

&lt;p&gt;That includes &lt;code&gt;config&lt;/code&gt;. Without &lt;code&gt;config&lt;/code&gt;, the tag is initialised with no measurement ID, so it has nothing to send and no reason to complain. Everything downstream still looks alive because everything downstream &lt;em&gt;is&lt;/em&gt; alive — the script really did load, the queue really is hooked, the commands really are in it. They're just sitting there.&lt;/p&gt;

&lt;p&gt;I didn't get this from the docs. It's just the only difference between the two rows of that table. It's also a decent rule of thumb: when a vendor snippet uses an unfashionable &lt;code&gt;function(){}&lt;/code&gt; where a modern one would do, assume the old form is doing something.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lint rules encode what's usually true. &lt;code&gt;prefer-rest-params&lt;/code&gt; is right nearly every time, and this is the case where "nearly" costs you all your data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;p&gt;Keep Google's original stub verbatim, and leave a note loud enough to survive the next cleanup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * **`arguments` here is load-bearing — do not "modernize" it into a rest
 * parameter.** gtag.js tells a command apart from a data-layer object by
 * checking the pushed value is a real `arguments` object. A plain array is
 * silently ignored: the tag loads, the queue fills, and not one hit is sent.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GtagFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="c1"&gt;// eslint-disable-next-line prefer-rest-params -- a rest array is not a command&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Mirror file: &lt;a href="https://github.com/AcruxCore/AcruxCore/blob/main/apps/web/src/lib/analytics.ts" rel="noopener noreferrer"&gt;&lt;code&gt;apps/web/src/lib/analytics.ts&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A comment alone wasn't enough for me here, because the failure is invisible and the "cleanup" that reintroduces it is a one-character edit. So the test asserts the exact property that matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queues each command as an `arguments` object, which is what gtag.js recognizes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stubGlobal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;window&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;gtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;G-TEST123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataLayer&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[object Arguments]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;IArguments&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;G-TEST123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rewrite the stub as a rest parameter and that test fails immediately with &lt;code&gt;[object Array]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Verified end to end against a real &lt;code&gt;vite build&lt;/code&gt;: accepting the consent banner produces a &lt;code&gt;gcs=G101&lt;/code&gt; hit and sets &lt;code&gt;_ga&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 3 — the site that worked was reporting to nobody
&lt;/h2&gt;

&lt;p&gt;While isolating bug 2 I went back to the docs site as a control, because it had looked healthy from day one. Its snippet was hand-written directly into &lt;code&gt;docusaurus.config.ts&lt;/code&gt; and never shared code with &lt;code&gt;apps/web&lt;/code&gt;, so it had the correct &lt;code&gt;arguments&lt;/code&gt; form all along.&lt;/p&gt;

&lt;p&gt;It was sending hits. It was sending them to the wrong property.&lt;/p&gt;

&lt;p&gt;Its measurement ID comes from a GitHub Actions secret that was never updated when the property changed, so every visit it recorded had been landing somewhere nobody was looking. No code change — update the secret, re-run the workflow — but it made the point for a third time in one week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Working and reporting where you're watching are two different states&lt;/strong&gt;, and only one of them shows up on a dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and after
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;before&lt;/th&gt;
&lt;th&gt;after&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GA4 tag in the production bundle&lt;/td&gt;
&lt;td&gt;absent&lt;/td&gt;
&lt;td&gt;present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sentry browser SDK in the production bundle&lt;/td&gt;
&lt;td&gt;absent&lt;/td&gt;
&lt;td&gt;present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gtag commands recognized by gtag.js&lt;/td&gt;
&lt;td&gt;0 of 3&lt;/td&gt;
&lt;td&gt;3 of 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hits reaching &lt;code&gt;/g/collect&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;page_view&lt;/code&gt;, &lt;code&gt;scroll&lt;/code&gt;, consent hits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;docs-site hits landing in the watched property&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;all of them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;checks that had reported a problem&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1 failing unit test if it regresses&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row is the one I'd frame. Before the fix, the total number of automated signals warning me about any of this was zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm carrying to the next codebase
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A repro has to copy the product's code path, not your memory of it.&lt;/strong&gt; I re-typed Google's snippet from the docs, which silently replaced the variable under test, and that one shortcut is the entire reason I wrote a bug report against Google. Paste the real function in and the two-page test finds it in ten minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing a second account felt like ruling something out. It ruled out nothing.&lt;/strong&gt; Both accounts ran the same broken client code, so the experiment could only ever return the same answer. A control group that shares the defect isn't a control group — and the confidence it gave me is what pushed me toward blaming Google instead of looking closer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assert the last link, not the layers.&lt;/strong&gt; I had a check for the env var, a check for the bundle, a check for the banner, and Google's own check for the tag. Four green lights, zero data. Every one of them verified a &lt;em&gt;precondition&lt;/em&gt; for a hit; none verified a hit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When one config gap swallows a variable, check every variable of the same shape.&lt;/strong&gt; &lt;code&gt;SENTRY_WEB_DSN&lt;/code&gt; was found only because it looked like &lt;code&gt;GA4_MEASUREMENT_ID&lt;/code&gt;, and it was the more dangerous of the two — nobody files a ticket saying "I'm getting suspiciously few errors."&lt;/p&gt;




&lt;p&gt;If you've got analytics or error monitoring wired into a monorepo build, two things are worth ten minutes today: grep your deployed bundle for the tag host, and check the type of what your &lt;code&gt;gtag&lt;/code&gt; stub actually pushes.&lt;/p&gt;

&lt;p&gt;What did yours turn out to be — an &lt;code&gt;Array&lt;/code&gt;, or &lt;code&gt;Arguments&lt;/code&gt;?&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Our Token Counter Took 26 Seconds on a Single Prompt</title>
      <dc:creator>Talha Anwar</dc:creator>
      <pubDate>Fri, 07 Aug 2026 23:37:36 +0000</pubDate>
      <link>https://dev.to/ailegend/our-token-counter-took-26-seconds-on-a-single-prompt-3l9p</link>
      <guid>https://dev.to/ailegend/our-token-counter-took-26-seconds-on-a-single-prompt-3l9p</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;I work on &lt;a href="https://acruxcore.com" rel="noopener noreferrer"&gt;AcruxCore&lt;/a&gt;, an LLM ops platform with an Express API gateway. Every completion passes a budget pre-check: estimate the token cost, compare against the team's remaining spend, reject if it doesn't fit.&lt;/p&gt;

&lt;p&gt;For OpenAI-family models, that estimate used &lt;a href="https://github.com/dqbd/tiktoken" rel="noopener noreferrer"&gt;&lt;code&gt;js-tiktoken&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOpenAiFamily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Fall through to the heuristic if the encoder rejects the input.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three lines. They do the right thing. They just don't always do it quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug Fix or Performance Improvement
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Performance bug — a quadratic BPE tokenizer on the synchronous request path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn't find this through a user complaint. Two &lt;code&gt;datasets&lt;/code&gt; tests started timing out at Jest's 5-second limit. I assumed I'd broken something, stashed my changes, checked out clean &lt;code&gt;staging&lt;/code&gt;, and ran them again. Still failed.&lt;/p&gt;

&lt;p&gt;The tests were fine. The code under them was just brutally slow.&lt;/p&gt;

&lt;p&gt;The BPE encoder is &lt;strong&gt;quadratic on whitespace-free input&lt;/strong&gt;. BPE works by repeatedly merging the most common adjacent pair of characters. Each pass scans the entire string to find the best pair. One merge per pass, full scan each time. A string of N characters with no whitespace means roughly N passes over N characters — N² work.&lt;/p&gt;

&lt;p&gt;Normal text never hits this because the pre-tokenizer splits on whitespace first. Every piece is one short word (~5 chars), so N² is nothing. A long unbroken string has no whitespace to split on, so the whole thing becomes one piece and N becomes the full length.&lt;/p&gt;

&lt;p&gt;Measured on &lt;code&gt;cl100k_base&lt;/code&gt; (GPT-4's tokenizer):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;single piece&lt;/th&gt;
&lt;th&gt;encode time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;256 chars&lt;/td&gt;
&lt;td&gt;4 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;512 chars&lt;/td&gt;
&lt;td&gt;18 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,024 chars&lt;/td&gt;
&lt;td&gt;70 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2,048 chars&lt;/td&gt;
&lt;td&gt;281 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9,000 chars&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.4 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20,000 chars&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26 s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Doubling the length roughly quadruples the time. Meanwhile, &lt;strong&gt;20,000 characters of ordinary prose encodes in 3 ms&lt;/strong&gt;. It's not the length — it's the lack of whitespace.&lt;/p&gt;

&lt;p&gt;This function runs on every single request. Node runs JavaScript on one thread. A 26-second synchronous encode doesn't just slow down one request — it freezes every other user on that process.&lt;/p&gt;

&lt;p&gt;And the input that triggers it isn't exotic: a base64 blob in a prompt, a minified JSON payload, a corrupted copy-paste. Any authenticated caller could stall the whole process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gateway completion, 2-char rendered variable:      82 ms
gateway completion, 9000-char rendered variable:  5506 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same code path. Same mocked provider. One template variable different.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not just remove the tokenizer?
&lt;/h3&gt;

&lt;p&gt;My first thought. But &lt;code&gt;estimateTokens&lt;/code&gt; has two jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Budget pre-check&lt;/strong&gt; — reject requests that would exceed the spend cap. Overestimate and you reject valid requests. Underestimate and teams blow past their budget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Billing&lt;/strong&gt; — when a provider streams without usage data, this estimate becomes the number on the user's bill.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And &lt;code&gt;chars / 4&lt;/code&gt; is a rough rule of thumb for English prose. It breaks on other inputs. Our test suite uses a synthetic 400 KB input (1,600 repetitions of 250 &lt;code&gt;x&lt;/code&gt; characters plus a space — the same one that caused the timeouts):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Characters: 1,600 × 251 = 401,600
Exact BPE:  52,800 tokens  →  401,600 / 52,800 = 7.6 chars/token
chars / 4:  100,400 tokens  →  401,600 / 4 = 100,400 (nearly 2× the truth)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your budget allows 80,000 tokens, the real prompt fits (52,800) but &lt;code&gt;chars / 4&lt;/code&gt; says it doesn't (100,400) and rejects it. Using &lt;code&gt;chars / 4&lt;/code&gt; everywhere would overestimate so aggressively that it would reject roughly half of all valid prompts.&lt;/p&gt;

&lt;p&gt;So the tokenizer stays. It just doesn't get to run unbounded anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The work happened in a private repo, so the PR link won't help. The fixed file is public in our mirror, with the measurement table in the doc comment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/AcruxCore/AcruxCore/blob/main/apps/api/src/gateway/providers/token-estimate.ts" rel="noopener noreferrer"&gt;&lt;code&gt;apps/api/src/gateway/providers/token-estimate.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; — the entire string in one encoder call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOpenAiFamily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* fall through to heuristic */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;heuristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt; — two decisions: how much text to encode, and how to chop it up first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOpenAiFamily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Bound 2: past 20,000 chars, encode a sample and scale the rest.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;MAX_BPE_SAMPLE_CHARS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;boundedEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sampleTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;boundedEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MAX_BPE_SAMPLE_CHARS&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sampleTokens&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;MAX_BPE_SAMPLE_CHARS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sampleTokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;MAX_BPE_SAMPLE_CHARS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* fall through to heuristic */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;heuristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My Improvements
&lt;/h2&gt;

&lt;p&gt;Two bounds. No new dependencies. No change to what it returns for normal input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bound 1 — cap each piece at 256 characters
&lt;/h3&gt;

&lt;p&gt;Nothing over 256 characters reaches the encoder. Longer pieces get &lt;code&gt;ceil(chars / 4)&lt;/code&gt; as a quick estimate instead.&lt;/p&gt;

&lt;p&gt;This removes the quadratic cost entirely, because 256 chars keeps each encoder call under ~4 ms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; 256 is a &lt;em&gt;threshold&lt;/em&gt;, not a chunk size. A 1,024-character piece is not split into four 256-char chunks. It skips the encoder entirely and gets &lt;code&gt;ceil(1024 / 4) = 256&lt;/code&gt; directly. Zero encoder calls.&lt;/p&gt;

&lt;p&gt;The text is only split at whitespace boundaries — to keep token counts accurate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;boundedEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pendingSeparator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(\s&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;pendingSeparator&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;piece&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pendingSeparator&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;pendingSeparator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MAX_BPE_PIECE_CHARS&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;heuristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pendingSeparator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;pendingSeparator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;MAX_BPE_PIECE_CHARS&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;heuristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pendingSeparator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pendingSeparator&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The easy line to get wrong is &lt;code&gt;pendingSeparator&lt;/code&gt;.&lt;/strong&gt; Whitespace must be attached to the &lt;em&gt;following&lt;/em&gt; word because tiktoken groups &lt;code&gt;" word"&lt;/code&gt; as one token. Encode the space on its own and &lt;code&gt;" the"&lt;/code&gt; becomes two tokens instead of one — every prose prompt gets overcounted by roughly its word count, and budget checks silently start rejecting requests they shouldn't.&lt;/p&gt;

&lt;p&gt;A correctness bug from a performance fix is a bad trade.&lt;/p&gt;

&lt;h4&gt;
  
  
  Effect on the 20,000-character blob
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;before&lt;/th&gt;
&lt;th&gt;after&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;encoder calls&lt;/td&gt;
&lt;td&gt;one, on 20,000 chars&lt;/td&gt;
&lt;td&gt;none (20,000 &amp;gt; 256)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;time&lt;/td&gt;
&lt;td&gt;26,952 ms&lt;/td&gt;
&lt;td&gt;0 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tokens returned&lt;/td&gt;
&lt;td&gt;2,500&lt;/td&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The count doubles — that's deliberate. &lt;code&gt;ceil(chars / 4)&lt;/code&gt; overestimates, but 20,000 identical characters isn't a real prompt, and for a budget check, overestimating is the safe direction.&lt;/p&gt;

&lt;p&gt;Ordinary text is untouched. &lt;code&gt;"Hello world"&lt;/code&gt; splits into &lt;code&gt;"Hello"&lt;/code&gt; and &lt;code&gt;" world"&lt;/code&gt;, both under 256, both encoded exactly as before.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bound 2 — cap the total at 20,000 characters
&lt;/h3&gt;

&lt;p&gt;Bound 1 only looks at one piece at a time. It never catches the case of many &lt;em&gt;short&lt;/em&gt; pieces adding up.&lt;/p&gt;

&lt;p&gt;1,600 repetitions of 250 &lt;code&gt;x&lt;/code&gt;s plus a space = 400 KB. Each piece is 250 chars (under the 256 cap), so Bound 1 lets every one through. At ~4 ms each, that's &lt;strong&gt;7 seconds&lt;/strong&gt;. Bound 1 never triggers once.&lt;/p&gt;

&lt;p&gt;Bound 2 fixes this: encode at most 20,000 characters total. For anything longer, encode the first 20K, measure the tokens-per-char ratio, and scale that ratio across the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step by step:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take the first 20,000 characters&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;boundedEncode&lt;/code&gt; on them (still uses Bound 1 internally)&lt;/li&gt;
&lt;li&gt;Get the token count from the sample&lt;/li&gt;
&lt;li&gt;Divide by 20,000 to get a &lt;code&gt;tokens-per-char&lt;/code&gt; ratio&lt;/li&gt;
&lt;li&gt;Multiply the ratio by the remaining characters&lt;/li&gt;
&lt;li&gt;Add sample tokens + tail estimate = return value&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Effect on the 400 KB input
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;bound 1 only&lt;/th&gt;
&lt;th&gt;both bounds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;characters encoded&lt;/td&gt;
&lt;td&gt;401,600&lt;/td&gt;
&lt;td&gt;20,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;time&lt;/td&gt;
&lt;td&gt;7,018 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;334 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tokens returned&lt;/td&gt;
&lt;td&gt;52,800&lt;/td&gt;
&lt;td&gt;52,771&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first 20K characters gave 2,628 tokens. Applied to the remaining 381,600 characters: 50,143 tokens. Total: 52,771 vs exact 52,800.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sampled ratio:  2,628 / 20,000 = 0.13 tokens per character
chars / 4 ratio:            1 / 4 = 0.25 tokens per character
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;0.13 is roughly half of 0.25. Using &lt;code&gt;chars / 4&lt;/code&gt; would claim 95,400 tokens on the tail instead of 50,143 — nearly double the truth. The sample already measured the real ratio, so using it is almost as accurate as encoding everything, without the 7-second cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;0.05% off, for 5% of the work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why 20,000 characters? At the 256-char piece cap, worst case is ~80 pieces (20,000 / 256). At ~4 ms each, that's ~320 ms — fast enough to stay on the request path. Anything bigger gets the sample treatment. The number isn't magic; it's where "encode everything" becomes "too slow."&lt;/p&gt;

&lt;p&gt;Worst case is now roughly &lt;strong&gt;350 ms for any input size.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Error direction
&lt;/h3&gt;

&lt;p&gt;Bound 1 can only &lt;strong&gt;overestimate&lt;/strong&gt; — &lt;code&gt;ceil(chars / 4)&lt;/code&gt; always returns more tokens than real BPE on the inputs that trigger it. For a budget check, that's the safe direction.&lt;/p&gt;

&lt;p&gt;Bound 2 is different: it extrapolates from a sample, so it can land slightly above or below the true count. The 400 KB case came out 0.05% low. But that only applies past 20,000 characters, and a fraction of a percent drift on a huge prompt is not a budget problem. A 7-second event-loop stall is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proving it didn't change the answer
&lt;/h3&gt;

&lt;p&gt;A bounded estimator that returns different numbers is a new bug, not a fix. Tests pin exactness first, speed second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exactness&lt;/strong&gt; — must match whole-string BPE on all normal input (under 20K chars):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;matches whole-string BPE counts for ordinary text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getEncoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cl100k_base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hi Alice, what is the weather in London?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The quick brown fox jumps over the lazy dog. &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;const x = foo.bar(baz, 42); // comment here&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nested value here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sample&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prose, code, JSON: identical counts. No existing estimate moved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Direction&lt;/strong&gt; — pathological input must never come back low:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;never underestimates the pathological input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getEncoding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cl100k_base&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeGreaterThanOrEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Speed&lt;/strong&gt; — one test per bound, so removing either guard fails immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stays fast on a long unbroken run of characters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warm up the encoder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeGreaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeLessThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stays fast when many pieces sit just under the per-piece cap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warm up the encoder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; `&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeGreaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;started&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeLessThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 1-second limit is deliberately generous — won't flake on loaded CI, but still fails instantly if someone removes a guard (the unfixed path takes minutes).&lt;/p&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;before&lt;/th&gt;
&lt;th&gt;after&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;50,000-char unbroken run&lt;/td&gt;
&lt;td&gt;minutes&lt;/td&gt;
&lt;td&gt;&amp;lt; 1 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400 KB of 250-char pieces&lt;/td&gt;
&lt;td&gt;7,018 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;334 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;completion, 9,000-char variable (provider mocked)&lt;/td&gt;
&lt;td&gt;5,506 ms&lt;/td&gt;
&lt;td&gt;~82 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;datasets&lt;/code&gt; test A&lt;/td&gt;
&lt;td&gt;6,340 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;410 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;datasets&lt;/code&gt; test B&lt;/td&gt;
&lt;td&gt;6,243 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;323 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The timing-out tests were never touched. They passed because the code underneath stopped being slow.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I'd carry to another codebase
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test adversarial input, not your own.&lt;/strong&gt; Every prompt I'd written was ordinary prose — the tokenizer's best case. Timing &lt;code&gt;'x'.repeat(20000)&lt;/code&gt; took ten minutes and found the whole bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A synchronous function on a request path is a shared resource.&lt;/strong&gt; "It's just a pure function over a string" stops being reassuring when the string is caller-controlled and the cost is superlinear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin the old answer before you optimize.&lt;/strong&gt; An exact-match test against whole-string encoding is how I caught the &lt;code&gt;pendingSeparator&lt;/code&gt; detail that would have silently overcounted every prompt.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you're running a BPE tokenizer anywhere synchronous, it's worth timing &lt;code&gt;encoder.encode('x'.repeat(20000))&lt;/code&gt; on your own setup today. What did you get?&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>performance</category>
      <category>llmops</category>
    </item>
  </channel>
</rss>
