DEV Community

Jack M
Jack M

Posted on

LLM Latency Budget: Make AI Features Feel Fast Without Burning Money

A slow AI feature does not feel smart. It feels broken.

That is the uncomfortable truth many AI SaaS builders hit after the demo works. The prototype answers well, the agent can call tools, and the RAG pipeline looks impressive. Then real users arrive. Prompts get longer. Queues form. Streaming starts late. One tenant uploads huge documents. Another runs bulk jobs at noon. Suddenly the same workflow that felt magical in testing feels like a spinner with an invoice attached.

The fix is not simply “use a faster model.” You need an LLM latency budget: a small set of rules that says how fast each AI workflow must feel, how many tokens it can spend, when to stream, when to cache, when to route to another model, and when to stop before cost and latency drift together.

This guide is for solo SaaS developers, micro SaaS builders, and AI SaaS teams shipping production features with LLM APIs, RAG, agents, or self-hosted models.

Why latency budgets matter now

AI platform news points in the same direction: builders are moving from chat demos to production workflows. Agent tools, web context APIs, voice agents, coding assistants, and RAG platforms are all getting more capable. At the same time, inference cost and reliability are under pressure.

Latency is now a product metric. Inference efficiency is becoming a business metric. Yet many articles stop at TTFT, TPOT, quantization, batching, or model serving. Fewer show how a SaaS builder turns those ideas into a product-level budget with code, dashboards, fallbacks, and customer-safe limits.

The simple model: TTFT, TPOT, and total time

You do not need a PhD in serving systems to start. Track three numbers.

Time to First Token

Time to First Token (TTFT) is the delay between the user action and the first streamed token. It includes network time, queue time, provider overhead, tool setup, retrieval, and the model’s prefill phase.

High TTFT is why a chat box feels dead.

Time Per Output Token

Time Per Output Token (TPOT) is the average time between generated tokens after the first token appears.

High TPOT is why streaming feels like a dripping tap.

End-to-end latency

End-to-end latency is the full time from request to final answer.

A rough formula is:

end_to_end_latency = TTFT + (output_tokens - 1) * TPOT
Enter fullscreen mode Exit fullscreen mode

That formula is not perfect for every provider, but it is good enough to reason about the user experience.

Build budgets by workflow, not by model

A common mistake is to set one global target like “AI responses must finish in 5 seconds.” That sounds clean but fails fast.

Different workflows need different budgets.

Workflow User expectation Suggested latency budget
Inline autocomplete Feels instant TTFT under 300ms, very short output
Chat answer Starts quickly TTFT under 1.5s, stream response
RAG answer with citations Trust matters TTFT under 3s, final answer under 15s
Agent with tool calls Progress matters First status under 1s, step updates every few seconds
Bulk document task Completion matters Async job, no chat-style waiting

The key is to budget for the experience, not the raw model call.

A user can forgive a 40-second background report if the UI says what is happening. The same user may abandon a 6-second inline writing assistant if nothing appears.

A practical LLM latency budget template

Create a budget object for each AI workflow.

{
  "workflow": "support_rag_answer",
  "max_ttft_ms": 2500,
  "max_total_ms": 15000,
  "max_input_tokens": 12000,
  "max_output_tokens": 900,
  "stream": true,
  "cache_policy": "semantic_and_exact",
  "fallback_model": "fast_general_model",
  "requires_citations": true,
  "async_after_ms": 12000
}
Enter fullscreen mode Exit fullscreen mode

This turns “make it faster” into engineering constraints. Your app can now decide whether to trim context, stream, route to a faster model, switch to async, reject an oversized request, or use a cached answer.

Instrument every request

Start by logging latency and token data for every AI request. Do this before buying another tool or changing providers.

Here is a small TypeScript-style example.

type LlmTrace = {
  requestId: string;
  tenantId: string;
  workflow: string;
  model: string;
  inputTokens: number;
  outputTokens: number;
  ttftMs: number | null;
  totalMs: number;
  costUsd: number;
  cacheHit: boolean;
  status: "success" | "timeout" | "error";
};

async function runWithTrace(input: {
  tenantId: string;
  workflow: string;
  prompt: string;
}) {
  const started = Date.now();
  let firstTokenAt: number | null = null;
  let output = "";

  const stream = await llm.stream({
    model: "fast-general",
    prompt: input.prompt,
    max_tokens: 700
  });

  for await (const chunk of stream) {
    if (!firstTokenAt) firstTokenAt = Date.now();
    output += chunk.text;
    sendToClient(chunk.text);
  }

  const finished = Date.now();

  const trace: LlmTrace = {
    requestId: crypto.randomUUID(),
    tenantId: input.tenantId,
    workflow: input.workflow,
    model: "fast-general",
    inputTokens: estimateTokens(input.prompt),
    outputTokens: estimateTokens(output),
    ttftMs: firstTokenAt ? firstTokenAt - started : null,
    totalMs: finished - started,
    costUsd: estimateCost(input.prompt, output),
    cacheHit: false,
    status: "success"
  };

  await saveTrace(trace);
  return output;
}
Enter fullscreen mode Exit fullscreen mode

Keep the trace simple. If you capture request ID, tenant ID, workflow, model, tokens, TTFT, total time, cost, cache hit, and status, you can answer most early performance questions.

Control input tokens before touching infrastructure

Long prompts hurt TTFT. Long context means more work before the first token appears.

For AI SaaS products, input bloat usually comes from full chat history, too many RAG chunks, raw HTML, unused tool descriptions, repeated system instructions, or entire customer records when only a few fields matter. Before optimizing GPUs or switching vendors, cut useless context.

Use a context packer.

type ContextItem = {
  id: string;
  text: string;
  priority: number;
  tokenEstimate: number;
};

function packContext(items: ContextItem[], maxTokens: number) {
  const sorted = [...items].sort((a, b) => b.priority - a.priority);
  const selected: ContextItem[] = [];
  let used = 0;

  for (const item of sorted) {
    if (used + item.tokenEstimate > maxTokens) continue;
    selected.push(item);
    used += item.tokenEstimate;
  }

  return selected;
}
Enter fullscreen mode Exit fullscreen mode

This is not fancy. That is the point. A basic priority-based packer often beats “send everything and hope.”

For RAG, use fewer, better chunks. For agents, expose fewer tools per step. For browser automation, clean the page before putting it into the prompt.

Cap output tokens by job type

Output tokens drive total latency and cost. Many AI features do not need long answers.

Set output caps by workflow:

  • Rewrite suggestion: 120 tokens
  • Error explanation: 250 tokens
  • Support answer: 700 tokens
  • Technical plan: 1,200 tokens
  • Background report: async job with a larger cap

Also give the model a structure that discourages rambling.

Answer in this format:
1. Direct answer: 2 sentences max
2. Steps: up to 5 bullets
3. Caveat: 1 short note if needed
Enter fullscreen mode Exit fullscreen mode

This improves scannability and reduces token drift.

Use streaming for perception, not as a bandage

Streaming can make an AI feature feel faster, but it does not fix everything.

Use streaming when:

  • The user is reading generated text
  • The answer may take more than 2 seconds
  • Partial output is useful
  • You can show citations or tool results after the draft begins

Do not rely on streaming when:

  • The workflow must return valid JSON
  • The user needs a single deterministic result
  • The model must complete tool calls before saying anything
  • You are hiding a slow retrieval or database step before the model starts

For agent workflows, stream status events, not only text.

{ "type": "status", "message": "Searching relevant docs" }
{ "type": "status", "message": "Checking account permissions" }
{ "type": "status", "message": "Drafting answer with citations" }
Enter fullscreen mode Exit fullscreen mode

This keeps users oriented while the system does real work.

Route models by latency class

Not every request deserves your strongest model.

Create latency classes:

Class Use case Model strategy
Instant autocomplete, labels, short rewrites smallest reliable model
Fast support chat, extraction, routing fast general model
Careful legal-ish, financial-ish, complex reasoning stronger model with tighter scope
Background reports, audits, batch enrichment slower model or queued worker

A simple router can start with rules.

function chooseModel(workflow: string, risk: "low" | "medium" | "high") {
  if (workflow === "autocomplete") return "small-fast";
  if (workflow === "bulk_report") return "batch-careful";
  if (risk === "high") return "careful-reasoning";
  return "fast-general";
}
Enter fullscreen mode Exit fullscreen mode

Later, you can route based on measured performance, tenant plan, queue depth, or failure rate. Start with rules that developers can understand and debug.

Cache the boring parts

Caching is one of the easiest ways to improve both latency and cost, but cache the right things.

Good cache candidates:

  • Embeddings for unchanged documents
  • RAG retrieval results for common queries
  • System prompt templates
  • Tool schemas
  • Classification outputs
  • Deterministic transformations
  • Answers to low-risk, repeated questions

Bad cache candidates:

  • Permission-sensitive answers without tenant scoping
  • Personalized answers without user scoping
  • Answers based on rapidly changing data
  • Outputs that may contain stale prices, policies, or account state

Always include tenant and permission context in cache keys.

function cacheKey(input: {
  tenantId: string;
  userRole: string;
  workflow: string;
  normalizedQuery: string;
  sourceVersion: string;
}) {
  return [
    input.tenantId,
    input.userRole,
    input.workflow,
    input.sourceVersion,
    hash(input.normalizedQuery)
  ].join(":");
}
Enter fullscreen mode Exit fullscreen mode

A cache hit that leaks data is worse than no cache.

Add graceful degradation

Your app needs a plan for bad days: provider slowness, queue spikes, long documents, or tenants running large jobs.

Useful degradation patterns:

  • Switch from careful model to fast model for low-risk requests
  • Reduce retrieved chunks when TTFT is at risk
  • Shorten output length during load spikes
  • Move long tasks to async jobs
  • Show partial results with “continue generating”
  • Ask the user to narrow the request before spending tokens

Example:

if (queueDepth > 100 && workflow === "support_rag_answer") {
  budget.max_input_tokens = 6000;
  budget.max_output_tokens = 500;
  budget.fallback_model = "fast-general";
}
Enter fullscreen mode Exit fullscreen mode

This is not about lowering quality everywhere. It is about protecting the experience under pressure.

Watch p95, not averages

Average latency lies. Your happy path can look fine while real users suffer.

Track these metrics by workflow and tenant tier:

  • p50 TTFT
  • p95 TTFT
  • p50 total latency
  • p95 total latency
  • input tokens per request
  • output tokens per request
  • cache hit rate
  • timeout rate
  • cost per successful task
  • retries per request

A simple alert rule is enough at first.

Alert when support_rag_answer p95 TTFT > 3000ms for 10 minutes.
Alert when cost per successful task rises 30% above 7-day baseline.
Alert when timeout rate > 2% for any paid tenant tier.
Enter fullscreen mode Exit fullscreen mode

Tie latency to cost. If p95 latency and cost both rise, you may have context bloat, retry loops, poor routing, or a workflow that should become async.

Treat retries as a budget risk

Retries feel harmless in code and expensive in production.

A retry can double cost, increase latency, and create duplicate tool actions. For agents, retry loops are even riskier because the model may call tools again.

Use retry rules:

  • Retry network errors with jitter
  • Do not retry validation failures blindly
  • Never retry write actions without idempotency keys
  • Stop after a small number of attempts
  • Log retry reason and added cost
const retryPolicy = {
  maxAttempts: 2,
  retryOn: ["rate_limit", "network_timeout"],
  neverRetryOn: ["invalid_json", "permission_denied", "policy_blocked"]
};
Enter fullscreen mode Exit fullscreen mode

If a workflow needs three retries to feel reliable, it probably needs a better design, not a bigger retry loop.

When to use async instead of chat

Some AI work should not pretend to be instant.

Use async jobs for:

  • Large document analysis
  • Multi-source research
  • Long agent workflows
  • Bulk enrichment
  • Report generation
  • Evaluation runs
  • Tasks with external API rate limits

A good async UX includes:

  • Immediate job receipt
  • Progress updates
  • Cancel button
  • Estimated completion window
  • Final summary
  • Error state that explains what happened

This protects your chat interface from becoming a waiting room.

Implementation checklist

Use this before shipping a new AI feature:

  • [ ] Define max TTFT and total latency by workflow
  • [ ] Set input and output token caps
  • [ ] Log tenant, workflow, model, tokens, cost, TTFT, total time, status
  • [ ] Track p95 latency, not only averages
  • [ ] Stream text or status events when useful
  • [ ] Route models by workflow and risk
  • [ ] Cache safe repeated work with tenant-aware keys
  • [ ] Trim context before changing infrastructure
  • [ ] Move long tasks to async jobs
  • [ ] Alert on latency, timeout rate, retry rate, and cost per successful task

Final thought

An LLM latency budget is not bureaucracy. It is a guardrail for product quality.

When budgets are missing, every prompt can grow, every agent can wander, every retry can double spend, and every slow request can look like a mystery. When budgets exist, your team can make clear tradeoffs: faster first token, shorter output, better context, safer cache, async workflow, or stronger model only where it matters.

Fast AI is not just about speed. It is about respecting the user’s time while protecting your margins.

FAQ

What is an LLM latency budget?

An LLM latency budget is a set of limits for an AI workflow: maximum time to first token, maximum total response time, input token cap, output token cap, model route, caching rule, and fallback behavior.

What is a good TTFT for AI features?

It depends on the workflow. Inline suggestions should feel almost instant. Chat answers should usually start streaming within one or two seconds. RAG or agent workflows can take longer if the UI shows useful progress.

How do I reduce LLM latency quickly?

Start by trimming input tokens, limiting output length, streaming responses, caching repeated work, and routing simple tasks to faster models. These changes are often easier than changing infrastructure.

Should every AI workflow stream output?

No. Streaming works well for readable text and progress updates. It is less useful for strict JSON, hidden tool-call workflows, or tasks where partial output could confuse the user.

How does latency relate to AI cost?

Long prompts, long outputs, retries, and tool loops usually increase both latency and cost. That is why production teams should track tokens, latency, cache hit rate, and cost per successful task together.

Is self-hosting faster than using an API?

Not automatically. Self-hosting can reduce control-plane uncertainty, but serving models well requires batching, memory management, scaling, monitoring, and hardware tuning. Measure TTFT, TPOT, and total cost before assuming self-hosting is better.

Top comments (0)