Siddhant Deval
Siddhant Deval
backend5 min read

The Anatomy of an LLM Call: Tokens, Context Windows & Probabilistic Mechanics

Engineers routinely measure AI performance by total request time alone, missing the critical distinction between TTFT and TPS. This article dissects BPE tokenisation, KV cache mechanics, and the pre-fill vs decode phase split that defines every latency budget in the Workspace streaming gateway.

Series·Part 2 of 24

Full-Stack AI Architecture: From Streaming Foundations to Resilient Agentic Systems in Node.js & React

The Anatomy of an LLM Call: Tokens, Context Windows & Probabilistic Mechanics

Every engineering decision in this series — how we size streaming buffers, schedule context windows, price caching investments, and govern agent budgets — descends from a single source of truth: what exactly happens inside an LLM API call at the millisecond level. Engineers who skip this article treat latency as an opaque black box, then spend weeks chasing phantom performance bottlenecks. This article eliminates that opacity.

We dissect four interconnected mechanisms: Byte-Pair Encoding tokenisation, the pre-fill versus decode phase split, KV cache reuse, and the probabilistic sampling loop that converts a probability distribution into the next token. Each connects directly to concrete API decisions in Parts 4–7.

Architectural Note

Who this is for: Full-stack engineers who call LLM APIs but measure performance only by total response time. After this article, you will instrument TTFT, ITL, and TPS as separate first-class metrics and make informed decisions about model selection, prompt structure, and caching strategy.


1. Tokenisation: From Characters to Vocabulary IDs

1.1 Byte-Pair Encoding (BPE) in Practice

LLMs do not see text. They see integer IDs from a fixed vocabulary. The tokeniser is the translation layer. Modern foundation models use BPE with vocabulary sizes between 50,000 and 256,000 tokens.

BPE encodes text by iteratively merging the most frequent character-pair sequences in training data until a target vocabulary size is reached. Common English words become single tokens; rare words, names, and non-ASCII characters fragment into multiple tokens.

TYPESCRIPT
// Practical tokenisation — using Tiktoken
// GPT-4o uses o200k_base (200k vocab); GPT-4 / GPT-3.5-turbo use cl100k_base (100k vocab)
import { encoding_for_model } from 'tiktoken'

// Note: encoding_for_model('gpt-4o') resolves to o200k_base
const enc = encoding_for_model('gpt-4o')

function tokenise(text: string): { ids: number[]; count: number } {
  const ids = enc.encode(text)
  return { ids: Array.from(ids), count: ids.length }
}

// Cost and latency examples:
console.log(tokenise('Hello, world!').count)         // → 4
console.log(tokenise('Héllo, wörld!').count)         // → 8  (non-ASCII ≈ 2× tokens)
console.log(tokenise('    '.repeat(10)).count)       // → 10 (each leading space = token)
console.log(tokenise('userId').count)                // → 1  (common camelCase merge)
console.log(tokenise('x_forwarded_for').count)       // → 5  (underscored tokens fragment)

The practical consequence: cost and latency are not proportional to character count. Code with heavy indentation, multilingual content, and long identifier names tokenises at 2–4× the cost of equivalent plain English prose.

1.2 The Three Cost Variables Engineers Control

Variable What You Control Token Impact
System prompt length Tool schema verbosity, instruction wordiness Fixed per-request overhead; amortise with prefix caching
Conversation history length How many prior turns you include Linear growth; prune with summarisation (Part 14)
Output max_tokens Upper bound on generation Hard billing ceiling; set conservatively with stop sequences
TYPESCRIPT
// ✅ Token-aware request builder — baseline for the Workspace gateway
// file: src/gateway/TokenAwareRequestBuilder.ts

// Use o200k_base for GPT-4o; cl100k_base for GPT-4/GPT-3.5. Using gpt-4o as a
// close approximation for all modern models (all share the same vocabulary size range).
import { encoding_for_model } from 'tiktoken'
import type { ChatMessage } from '../types'

const MODEL_TOKEN_LIMITS: Record<string, number> = {
  'claude-3-5-sonnet-20241022': 200_000,
  'gpt-4o': 128_000,
  'gpt-4o-mini': 128_000,
}

export class TokenAwareRequestBuilder {
  private enc = encoding_for_model('gpt-4o') // o200k_base (200k vocab) — close approximation for all modern models

  countTokens(text: string): number {
    return this.enc.encode(text).length
  }

  countMessagesTokens(messages: ChatMessage[]): number {
    // Each message has 4 tokens overhead: role, name, content separator, delimiters
    return messages.reduce(
      (total, msg) => total + this.countTokens(msg.content) + 4,
      3, // Priming tokens for every request
    )
  }

  buildWithBudget(
    systemPrompt: string,
    history: ChatMessage[],
    model: string,
    maxOutputTokens: number,
  ): ChatMessage[] {
    const limit = MODEL_TOKEN_LIMITS[model] ?? 128_000
    const systemTokens = this.countTokens(systemPrompt) + 4
    const availableForHistory = limit - systemTokens - maxOutputTokens - 100 // safety margin

    // Prune from oldest turn, preserving the last user message
    const pruned: ChatMessage[] = []
    let consumed = 0

    for (let i = history.length - 1; i >= 0; i--) {
      const cost = this.countTokens(history[i].content) + 4
      if (consumed + cost > availableForHistory) break
      pruned.unshift(history[i])
      consumed += cost
    }

    return [{ role: 'system', content: systemPrompt }, ...pruned]
  }
}

2. The Two Phases of Inference: Pre-fill and Decode

2.1 Phase Split and What It Means for Latency

A single LLM API call has two fundamentally different computational phases. Most engineers measure only the total time. That is the wrong metric.

Metric Phase Scales With Engineering Lever
TTFT (Time-to-First-Token) Pre-fill Input token count, model size, GPU memory Prefix caching; shorter system prompts
ITL (Inter-Token Latency) Decode Memory bandwidth of GPU Model quantisation; output length limits
TPS (Tokens Per Second) Decode 1 / ITL Model selection; provider tier
Total Latency Both Input + Output tokens All of the above
Crucial Requirement

For streaming chat UIs, TTFT is the metric users feel. A 5-second TTFT with fast ITL feels worse than a 1.5-second TTFT with moderate ITL. React 18 streaming UIs must report TTFT as a Span metric (Part 23) and adjust loading indicators accordingly — a spinner that disappears at TTFT, then progressive text, is dramatically better UX than waiting for total completion.

2.2 Measuring TTFT and ITL in the Gateway

TYPESCRIPT
// ✅ Correct Pattern — Streaming metrics instrumented at the gateway
// file: src/gateway/MeteredStream.ts

export interface StreamMetrics {
  ttft: number      // ms from request sent to first token received
  itl: number[]     // ms between each successive token
  tps: number       // tokens per second (average decode phase)
  totalTokens: number
  totalMs: number
}

export async function* meteredStream(
  stream: AsyncIterable<string>,
  requestSentAt: number,
): AsyncGenerator<string, StreamMetrics> {
  let firstToken = true
  let ttft = 0
  const itl: number[] = []
  let prevTokenAt = 0
  let totalTokens = 0

  for await (const token of stream) {
    const now = performance.now()

    if (firstToken) {
      ttft = now - requestSentAt
      prevTokenAt = now
      firstToken = false
    } else {
      itl.push(now - prevTokenAt)
      prevTokenAt = now
    }

    totalTokens++
    yield token
  }

  const totalMs = performance.now() - requestSentAt
  const decodeMs = totalMs - ttft
  const tps = totalTokens > 1 ? ((totalTokens - 1) / decodeMs) * 1000 : 0

  return { ttft, itl, tps, totalTokens, totalMs }
}

3. KV Cache: The Billion-Dollar Optimisation

3.1 What the KV Cache Is

During the pre-fill phase, the transformer computes a Key (K) and Value (V) matrix for every token in every attention layer. These matrices are the memory of the attention mechanism. Without caching, every new decode step re-computes them for all prior tokens — quadratic complexity.

The KV cache stores these K/V matrices in GPU VRAM so the decode phase only computes new tokens. This is what makes autoregressive generation feasible at all. At the provider level, it is also the foundation of prompt prefix caching.

3.2 Prompt Prefix Caching: The Cost Reduction That Pays for Itself

Modern providers (Anthropic, OpenAI) extend KV caching across API requests. If consecutive requests share an identical byte-prefix in the prompt, the provider reuses the cached K/V matrices — reducing pre-fill compute cost by 80–90% for the cached portion.

The critical constraint: the cached prefix must be byte-identical. A single character change anywhere in the system prompt or tool schemas invalidates the entire cache for all tokens after that point.

TYPESCRIPT
// ✅ Prefix-cache-stable system prompt construction
// file: src/gateway/SystemPromptBuilder.ts

export class SystemPromptBuilder {
  // STABLE SECTION — Must never change between requests for the same org.
  // Placed first to maximise prefix cache coverage.
  private buildStablePrefix(orgName: string, tools: string): string {
    return [
      `You are the Enterprise Collaborative AI Workspace assistant for ${orgName}.`,
      'You help engineers draft technical documents, create architecture diagrams, and retrieve knowledge.',
      '',
      '## Capabilities',
      tools,  // Tool schemas — must be deterministically serialised
      '',
      '## Response Rules',
      '- Always respond in structured JSON matching the WorkspaceAction schema.',
      '- Never fabricate document content. Ground all claims in retrieved knowledge.',
    ].join('\n')
  }

  // DYNAMIC SECTION — Placed last; not cached.
  private buildDynamicSuffix(userContext: { name: string; timezone: string }): string {
    return `\n## Current Session\nUser: ${userContext.name}\nTimezone: ${userContext.timezone}`
  }

  build(orgName: string, tools: string, userContext: { name: string; timezone: string }): string {
    // Stable prefix first → maximises KV cache hit rate
    // Dynamic suffix last → only un-cached portion re-computed each call
    return this.buildStablePrefix(orgName, tools) + this.buildDynamicSuffix(userContext)
  }
}

3.3 Practical KV Cache Sizing and VRAM

VRAM for KV cache ≈ 2 × num_layers × num_heads × head_dim × context_length × bytes_per_element

For a 70B parameter model (typical cloud hosted):
  2 × 80 × 8 × 128 × 100,000 × 2 bytes (fp16) ≈ 32 GB for 100k token context

This is why long-context models are expensive to host — KV cache memory grows linearly with context length. Every 10k tokens of context you add costs approximately 3.2 GB of VRAM. At the API level, you pay for this with higher per-token pricing on longer contexts.


4. The Sampling Loop: Probability to Token

4.1 How Temperature and Top-P Shape Output

After pre-fill, each decode step runs the same process:

  1. Forward pass through the transformer with current K/V cache → produces a logit vector (one scalar per vocabulary token).
  2. Apply temperature scaling: logits / temperature.
  3. Apply Top-P nucleus sampling: keep only the smallest token set whose cumulative probability ≥ p.
  4. Sample from the remaining probability distribution.
  5. Append the sampled token ID to the sequence; update K/V cache.
  6. If the token is the EOS token (or max_tokens reached), stop.

4.2 What This Means for API Configuration

Parameter Effect Production Recommendation
temperature: 0.0 Deterministic argmax (greedy) Structured output tasks: schema generation, SQL, code
temperature: 0.3–0.7 Controlled creativity Document drafting, summarisation in the Workspace
temperature: 1.0+ High diversity, frequent incoherence Avoid in production; useful only for synthetic data generation
top_p: 0.95 Standard nucleus — good default Use with temperature, not instead of it
max_tokens Hard decode stop Always set; no default protects against runaway generation
TYPESCRIPT
// ✅ Type-safe model config for different task profiles
// file: src/gateway/ModelConfigs.ts

export type TaskProfile = 'structured_output' | 'document_draft' | 'agent_plan' | 'embedding'

export const MODEL_CONFIGS: Record<TaskProfile, ModelConfig> = {
  structured_output: {
    model: 'claude-3-5-sonnet-20241022',
    temperature: 0,
    max_tokens: 2048,
    // Use JSON Schema constrained decoding when provider supports it
  },
  document_draft: {
    model: 'claude-3-5-sonnet-20241022',
    temperature: 0.4,
    max_tokens: 8192,
  },
  agent_plan: {
    model: 'claude-3-5-sonnet-20241022',
    temperature: 0.1,      // Near-deterministic planning
    max_tokens: 4096,
  },
  embedding: {
    model: 'text-embedding-3-large',
    dimensions: 1536,      // Matryoshka: reducible to 256 for fast ANN
  },
} as const

5. Connecting Mechanics to Architecture: The Workspace Implications

Every mechanism in this article has a direct architectural consequence in the Workspace system:

Mechanism Workspace Impact Series Article
BPE cost variability Code-heavy prompts cost 2–4× more; tool schemas need size discipline Part 3, Part 16
TTFT ≠ Total Latency SSE clients must show first-token spinner, not completion spinner Part 6
Pre-fill scales with input RAG chunks are expensive to stuff; budget is mandatory Part 14
Prefix caching requires stability System prompt and tool schemas serialised deterministically Part 3, Part 14
KV cache is VRAM-bound Long-context is not free; context window scheduling is an economic lever Part 14
Sampling is stochastic temperature: 0 for schema tasks; never assume repeatability Part 3

Summary

Concept Rule
Token Cost Count tokens, not characters. Code, non-ASCII text, and indentation tokenise at 2–4× premium.
TTFT The metric users feel in streaming UIs. Reduce by shortening system prompts and leveraging prefix caching.
Pre-fill Scales with input length. Every RAG chunk, history turn, and tool schema has a pre-fill cost.
Prefix Cache Maximise by placing stable content (system prompt, tool schemas) first and byte-identically across requests.
Temperature Set 0 for structured/schema tasks; 0.3–0.7 for prose; always set max_tokens.

What's Next

Part 3 converts the probabilistic mechanics into a formal contract system. We implement grammar-constrained decoding, Zod strict schema validation with .strict(), and an AST self-repair pipeline that recovers from streaming truncation — the structural invariant gate that sits at the heart of the Workspace trust boundary.

Research & Synthesis Note

This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.

#LLM Internals#Tokenisation#KV Cache#Latency#Node.js#Performance
Siddhant Deval

Written by Siddhant Deval

Senior Full-Stack Engineer building high-scale architectures, browser performance engineering systems, and SaaS platforms.

Series·Part 2 of 24

Full-Stack AI Architecture: From Streaming Foundations to Resilient Agentic Systems in Node.js & React

Curriculum Roadmap
01The Full-Stack AI Paradigm Shift: Beyond Toy Wrappers & Prompt Concatenation
02The Anatomy of an LLM Call: Tokens, Context Windows & Probabilistic Mechanics
Reading
03Structured Outputs & Formal Invariants: Taming Non-Determinism with Zod & TypeBox
04Full-Stack Streaming Protocols: SSE, Fetch Streams & WebSockets
05V8 Streams, Server-Side Backpressure & Cancellations in Node.js
06React 18/19 Streaming UI & Generative Components
07Generative UI & Interactive Canvas Synchronization
08High-Dimensional Vector Embeddings & Similarity Math
09Vector Storage & Indexing at Scale: HNSW vs IVF in PostgreSQL (pgvector)
10Hybrid Search: Dense Vectors Meet Sparse Lexical BM25
11Multi-Stage Retrieval & Cross-Encoder Reranking
12Advanced RAG Architectures: Contextual Retrieval & Parent-Document Splitting
13Query Transformation, Decomposition & Multi-Query Expansion
14Dynamic Context Window Management & Long-Context Packing
15Agent Loops & Deterministic State Machines
16Model Context Protocol (MCP) in Full-Stack Architecture
17Human-in-the-Loop & Durable Agent Workflows
18Multi-Agent Orchestration & Collaborative Swarms
19Semantic Caching & Provider Cost Optimization
20Multi-Provider AI Gateways: Fallbacks, Rate Limiting & Hedged Requests
21Client-Side AI & Edge Hybrid Execution: WebGPU & Transformers.js
22Production Evals & Guardrails: Measuring Retrieval and Generation
23Full-Stack AI Observability & Tracing with OpenTelemetry
24Production Capstone: Assembling The Enterprise Collaborative AI Workspace