LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 196 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnApplied LLM EngineeringLLM Cost Engineering & Token Economics
⚙️MediumMLOps & Deployment

LLM Cost Engineering & Token Economics

Build an auditable LLM cost ledger from usage traces, cache decisions, output contracts, offline batch work, and release budget gates.

20 min read
Learning path
Step 83 of 196 in the full curriculum
Semantic Caching & Cost OptimizationModel Gateways, Routing, and Fallbacks

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

In our worked PolicyOps release receipt, an on-call alert fires at 09:12: one traffic day plus its nightly evaluation costs $23.92. A 30-day projection still fits under the $750 budget, but leaves only $32.44. The release can pass its hard gate and still deserve a closer look.

The dashboard labels 3,200 requests as cache hits. Some returned an already evaluated answer and made no model call. Others reused a provider prompt prefix and still generated new text. Collapse those events into one label and you can't tell avoided spend from discounted spend, or know which correctness check failed.

PolicyOps's support assistant is built around a large language model (LLM). It also handles live-order lookups and cited exception answers. We need a cost ledger for one evaluated release: provider-reported token usage, both cache kinds, output savings that preserve the answer contract, work that can wait, and a promotion decision. The next lesson will consume that budget contract when it routes and falls back. First classify each call.

The accounting boundary

Before pricing, classify what happened. "Cache hit" can mean no model call or a generated answer with cheaper input. Use one row for each outcome:

DecisionWhat executes?What belongs in this ledger?
Semantic answer hit from the previous lessonStored evaluated answer returnedNo generation charge; record avoided generation separately
Generated answer with provider prompt-cache hitModel still produces a new answerFresh input, cached input, and output token charges
Generated answer without prompt-cache hitModel reads full input and produces a new answerFresh input and output token charges
Offline evaluation batchModel runs later, outside interactive pathBatch-eligible token spend and completion contract

Only the semantic answer hit has no generated usage. A provider prompt cache reuses matching prefix work, then generates a fresh answer. That distinction determines both the invoice row and the correctness test you need.

Ask two questions in order: did we return an evaluated answer, and if not, did the provider reuse an input prefix? The flow below turns those answers into billable categories.

Diagram showing Support request, Safe stored answer?, No generation charge, and Generate new answer.
Support request, Safe stored answer?, No generation charge, and Generate new answer.

Use a dated rate card, not remembered prices

Treat provider pricing as versioned configuration. This lab's teaching fixture freezes OpenAI's GPT-5.4 short-context row. Checked against the official pricing and model pages on August 21, 2026, it matches the May 31, 2026 snapshot used in the code: standard input $2.50 per million tokens, cached input $0.25 per million, and output $15.00 per million.[1]Reference 1OpenAI API Pricinghttps://developers.openai.com/api/docs/pricing[2]Reference 2GPT-5.4 Modelhttps://developers.openai.com/api/docs/models/gpt-5.4

OpenAI also publishes a long-context row. Above 272K input tokens, the full session uses $5.00 / $0.50 / $22.50 for input, cached input, and output. Every trace here stays below that cliff, so the ledger selects the short-context row before doing any token arithmetic.[1]Reference 1OpenAI API Pricinghttps://developers.openai.com/api/docs/pricing[2]Reference 2GPT-5.4 Modelhttps://developers.openai.com/api/docs/models/gpt-5.4

Batch rates are another published row, not a local "divide every Standard rate by two" rule. The displayed Batch cached-input rate is $0.13 per million tokens; halving $0.25 gives $0.125, so local arithmetic would lose the displayed rate. This dated fixture teaches ledger shape, not a permanent price promise. Refresh production cards from current provider documentation before forecasting a release.[3]Reference 3OpenAI Batch API Guidehttps://developers.openai.com/api/docs/guides/batch[1]Reference 1OpenAI API Pricinghttps://developers.openai.com/api/docs/pricing

The usage trace, not a character estimate, settles the bill after a model call. Read total input tokens, the cached subset, and output tokens from that trace. Fresh input is total input minus cached input.

Reasoning models add a tempting accounting trap. For GPT-5.4, provider-reported output_tokens already includes hidden reasoning tokens billed at the output rate. output_tokens_details.reasoning_tokens explains part of that total; adding it again would double-charge. A call can also hit max_output_tokens while thinking, leaving no visible answer while its output tokens still bill.[4]Reference 4Reasoning modelshttps://developers.openai.com/api/docs/guides/reasoning

Before running the first cell, predict the split for a 1,800-token trace with 400 cached tokens: 1,400 fresh tokens and 400 cached tokens. The rate card and usage objects make that subtraction explicit.

01-rate-card-and-usage.py
1from collections import defaultdict 2from dataclasses import dataclass, replace 3from decimal import Decimal, ROUND_HALF_UP 4 5MILLION = Decimal("1000000") 6CENT = Decimal("0.01") 7 8def dollars(amount: Decimal) -> Decimal: 9 return amount.quantize(CENT, rounding=ROUND_HALF_UP) 10 11@dataclass(frozen=True) 12class TokenRates: 13 input_per_1m: Decimal 14 cached_input_per_1m: Decimal 15 output_per_1m: Decimal 16 17@dataclass(frozen=True) 18class RateCard: 19 rate_card_id: str 20 standard: TokenRates 21 batch: TokenRates 22 23@dataclass(frozen=True) 24class Usage: 25 input_tokens: int 26 cached_input_tokens: int 27 output_tokens: int 28 reasoning_tokens: int = 0 29 30 def __post_init__(self) -> None: 31 if not 0 <= self.cached_input_tokens <= self.input_tokens: 32 raise ValueError("cached input must be a subset of input tokens") 33 if min(self.input_tokens, self.output_tokens) < 0: 34 raise ValueError("token counts cannot be negative") 35 if not 0 <= self.reasoning_tokens <= self.output_tokens: 36 raise ValueError("reasoning tokens must be a subset of output tokens") 37 38rate_card = RateCard( 39 rate_card_id="openai-gpt-5.4-short-context-2026-05-31", 40 standard=TokenRates( 41 input_per_1m=Decimal("2.50"), 42 cached_input_per_1m=Decimal("0.25"), 43 output_per_1m=Decimal("15.00"), 44 ), 45 batch=TokenRates( 46 input_per_1m=Decimal("1.25"), 47 cached_input_per_1m=Decimal("0.13"), 48 output_per_1m=Decimal("7.50"), 49 ), 50) 51 52print(rate_card.rate_card_id) 53print(f"standard_input_per_1m=${rate_card.standard.input_per_1m}") 54print(f"standard_cached_input_per_1m=${rate_card.standard.cached_input_per_1m}") 55print(f"standard_output_per_1m=${rate_card.standard.output_per_1m}") 56print(f"batch_input_per_1m=${rate_card.batch.input_per_1m}") 57print(f"batch_cached_input_per_1m=${rate_card.batch.cached_input_per_1m}") 58print(f"batch_output_per_1m=${rate_card.batch.output_per_1m}")
Output
1openai-gpt-5.4-short-context-2026-05-31 2standard_input_per_1m=$2.50 3standard_cached_input_per_1m=$0.25 4standard_output_per_1m=$15.00 5batch_input_per_1m=$1.25 6batch_cached_input_per_1m=$0.13 7batch_output_per_1m=$7.50

Price one generated answer

PolicyOps's public-policy-answer path uses a long evaluated instruction prefix and cites policy evidence in every generated response. Take the stable public question "How long are revoked access tokens retained?" A cold request reports 1,800 input tokens and 180 output tokens. Before looking at the formula, predict which side costs more: output has the higher rate, but input has ten times as many tokens.

Split each billable category instead of multiplying one blended token count by one price.

cost=fresh input tokens×fresh input price1,000,000+cached input tokens×cached input price1,000,000+output tokens×output price1,000,000\begin{aligned} \text{cost} = &\frac{\text{fresh input tokens} \times \text{fresh input price}}{1{,}000{,}000} \\ &+ \frac{\text{cached input tokens} \times \text{cached input price}}{1{,}000{,}000} \\ &+ \frac{\text{output tokens} \times \text{output price}}{1{,}000{,}000} \end{aligned}cost=​1,000,000fresh input tokens×fresh input price​+1,000,000cached input tokens×cached input price​+1,000,000output tokens×output price​​
02-price-one-generated-answer.py
1@dataclass(frozen=True) 2class PricedUsage: 3 fresh_input_usd: Decimal 4 cached_input_usd: Decimal 5 output_usd: Decimal 6 7 @property 8 def total_usd(self) -> Decimal: 9 return self.fresh_input_usd + self.cached_input_usd + self.output_usd 10 11def price_usage(usage: Usage, card: RateCard = rate_card, *, batch: bool = False) -> PricedUsage: 12 rates = card.batch if batch else card.standard 13 fresh_input_tokens = usage.input_tokens - usage.cached_input_tokens 14 return PricedUsage( 15 fresh_input_usd=Decimal(fresh_input_tokens) * rates.input_per_1m / MILLION, 16 cached_input_usd=Decimal(usage.cached_input_tokens) * rates.cached_input_per_1m / MILLION, 17 output_usd=Decimal(usage.output_tokens) * rates.output_per_1m / MILLION, 18 ) 19 20cold_policy_answer = Usage(input_tokens=1_800, cached_input_tokens=0, output_tokens=180) 21cold_cost = price_usage(cold_policy_answer) 22 23assert cold_cost.total_usd == Decimal("0.0072") 24assert rate_card.standard.output_per_1m > rate_card.standard.input_per_1m 25 26print(f"fresh_input=${cold_cost.fresh_input_usd:.6f}") 27print(f"output=${cold_cost.output_usd:.6f}") 28print(f"total=${cold_cost.total_usd:.6f}") 29print(f"output_share={(cold_cost.output_usd / cold_cost.total_usd):.1%}")
Output
1fresh_input=$0.004500 2output=$0.002700 3total=$0.007200 4output_share=37.5%

The output token rate is higher, but this 1,800-input-token request still spends more dollars on input because it has ten times as many input tokens as output tokens. Both volume and per-category rate belong in the ledger.

That split also gives you two different latency levers. Prefix reuse can reduce repeated input processing and first-token delay, but it doesn't remove decode. A shorter compliant answer can reduce output spend and often decode time. Measure time to first token, output tokens, and end-to-end latency separately instead of treating one cost change as a latency result.[5]Reference 5Efficient Memory Management for Large Language Model Serving with PagedAttention.https://arxiv.org/abs/2309.06180[1]Reference 1OpenAI API Pricinghttps://developers.openai.com/api/docs/pricing[6]Reference 6Prompt cachinghttps://developers.openai.com/api/docs/guides/prompt-caching

Reasoning tokens ride inside that output charge. In OpenAI's published usage shape, output_tokens=1186 includes reasoning_tokens=1024, leaving 162 output tokens outside the reasoning detail in this fixture. Predict the price before running the cell: charge 1,186 output tokens once, not 1,186 plus 1,024. An incomplete call can spend all 1,024 output tokens on hidden reasoning and still bill decode without returning a visible answer.

02b-price-reasoning-without-double-counting.py
1reasoning_answer = Usage( 2 input_tokens=1_800, 3 cached_input_tokens=0, 4 output_tokens=1_186, 5 reasoning_tokens=1_024, 6) 7incomplete_reasoning = Usage( 8 input_tokens=1_800, 9 cached_input_tokens=0, 10 output_tokens=1_024, 11 reasoning_tokens=1_024, 12) 13reasoning_cost = price_usage(reasoning_answer) 14incomplete_cost = price_usage(incomplete_reasoning) 15double_counted_output = Decimal("2210") * rate_card.standard.output_per_1m / MILLION 16 17assert reasoning_cost.output_usd == Decimal("1186") * rate_card.standard.output_per_1m / MILLION 18assert reasoning_cost.output_usd != double_counted_output 19assert incomplete_cost.output_usd == Decimal("1024") * rate_card.standard.output_per_1m / MILLION 20assert incomplete_cost.total_usd > Decimal("0") 21 22print(f"visible_plus_reasoning_output_tokens={reasoning_answer.output_tokens}") 23print(f"reasoning_detail={reasoning_answer.reasoning_tokens}") 24print(f"priced_output=${reasoning_cost.output_usd:.6f}") 25print(f"double_counted_output=${double_counted_output:.6f}") 26print(f"incomplete_hidden_output_tokens={incomplete_reasoning.output_tokens}") 27print(f"incomplete_total=${incomplete_cost.total_usd:.6f}")
Output
1visible_plus_reasoning_output_tokens=1186 2reasoning_detail=1024 3priced_output=$0.017790 4double_counted_output=$0.033150 5incomplete_hidden_output_tokens=1024 6incomplete_total=$0.019860

The incomplete row still includes the $0.004500 fresh-input charge. Hidden thinking isn't free, and reasoning_tokens isn't a second billable bucket. Keep the detail field for diagnosis: it explains why a response was expensive or stopped before visible output.

Prefix reuse is still a generated answer

Prompt-cache eligibility is observed behavior, not a budget promise. OpenAI's guidance, checked on August 21, 2026, gives GPT-5.6 and later a 1,024-visible-token minimum and charges cache writes at 1.25× the uncached input rate. GPT-5.4 follows earlier-model behavior: 2,048 visible tokens is the documented minimum, some earlier models may occasionally cache shorter prefixes, and there is no additional cache-write fee. Copying the GPT-5.4 ledger onto GPT-5.6 without a write column would under-count.[2]Reference 2GPT-5.4 Modelhttps://developers.openai.com/api/docs/models/gpt-5.4[6]Reference 6Prompt cachinghttps://developers.openai.com/api/docs/guides/prompt-caching

Caching also depends on an exact matching rendered prefix. Count a read only when returned usage reports cached_tokens > 0; put stable instructions and shared evidence first, then dynamic customer data. A prefix hit isn't a stored response, so the support model still answers the current question and still incurs output spend.[6]Reference 6Prompt cachinghttps://developers.openai.com/api/docs/guides/prompt-caching

The fixture below records an observed 1,280-token cache read. Treat that number as trace data, not as a guarantee that a prompt near any threshold will hit. Predict the arithmetic first: 1,800 total input tokens means 520 fresh tokens, while output remains 180 tokens. Any latency improvement here comes from input reuse, not from skipping decode.

03-account-for-prefix-reuse.py
1prefix_cached_policy_answer = Usage( 2 input_tokens=1_800, 3 cached_input_tokens=1_280, 4 output_tokens=180, 5) 6prefix_cost = price_usage(prefix_cached_policy_answer) 7savings = cold_cost.total_usd - prefix_cost.total_usd 8 9assert prefix_cost.total_usd == Decimal("0.004320") 10assert prefix_cost.output_usd == cold_cost.output_usd 11assert savings == Decimal("0.002880") 12 13print(f"cold_generation=${cold_cost.total_usd:.6f}") 14print(f"prefix_cached_generation=${prefix_cost.total_usd:.6f}") 15print(f"saved_per_generated_answer=${savings:.6f}") 16print(f"still_generated={prefix_cost.output_usd > 0}")
Output
1cold_generation=$0.007200 2prefix_cached_generation=$0.004320 3saved_per_generated_answer=$0.002880 4still_generated=True

Why does a provider prompt-cache hit still have an output charge?

Answer

It reuses processing for an exactly matching input prefix. The model still generates the new response, so output tokens remain billable.

Carry forward the semantic-cache decision

The preceding lesson approved semantic response reuse only for stable public-policy answers within one evaluated release scope. This ledger records that decision; it doesn't rebuild the vector index or threshold.

For "How long are revoked access tokens retained?", a stored-answer hit records zero generated tokens. A prompt-prefix hit records a new answer with cheaper input. Predict those rows before running the next cell: the first has $0 actual generation and a non-zero counterfactual, while the second has $0.004320 actual generation.

04-separate-answer-hits-from-prefix-hits.py
1@dataclass(frozen=True) 2class RequestOutcome: 3 feature: str 4 decision: str 5 usage: Usage | None 6 counterfactual_usage: Usage | None = None 7 8def generated_cost(outcome: RequestOutcome) -> Decimal: 9 if outcome.usage is None: 10 return Decimal("0") 11 return price_usage(outcome.usage).total_usd 12 13semantic_hit = RequestOutcome( 14 feature="public-policy-answer", 15 decision="SEMANTIC_ANSWER_HIT", 16 usage=None, 17 counterfactual_usage=prefix_cached_policy_answer, 18) 19prefix_hit = RequestOutcome( 20 feature="public-policy-answer", 21 decision="GENERATE_PREFIX_HIT", 22 usage=prefix_cached_policy_answer, 23) 24 25avoided_generation = price_usage(semantic_hit.counterfactual_usage).total_usd 26assert generated_cost(semantic_hit) == Decimal("0") 27assert generated_cost(prefix_hit) == Decimal("0.004320") 28 29print(f"semantic_hit_generation=${generated_cost(semantic_hit):.6f}") 30print(f"semantic_hit_avoided=${avoided_generation:.6f}") 31print(f"prefix_hit_generation=${generated_cost(prefix_hit):.6f}")
Output
1semantic_hit_generation=$0.000000 2semantic_hit_avoided=$0.004320 3prefix_hit_generation=$0.004320
Three dollar stacks for the public-policy question How long are revoked access tokens retained? Cold generation bills $0.007200 as $0.004500 fresh input plus $0.002700 output. Prefix reuse bills $0.004320 as $0.001300 fresh, $0.000320 cached, and the same $0.002700 output. A stored-answer hit realizes $0.000000 and keeps the $0.004320 prefix bill as avoided, not charged.
Same public-policy question, three ledgers. Prefix reuse shrinks input and still bills the $0.002700 decode. A stored answer realizes $0.000000; the $0.004320 bar is avoided spend, not a credit.

Replay a day of traffic

A per-request price hides traffic shape. Replay one PolicyOps day before testing a shorter exception answer:

  • 3,200 stable policy questions safely hit the semantic answer cache.
  • 1,800 policy questions still generate, but reuse the evaluated prompt prefix.
  • 3,000 live-order questions must generate from current tool evidence.
  • 500 access exceptions generate a longer cited answer with the stable prefix.

Before running the replay, predict the ranking. Semantic hits are numerous but have no generated spend. Live-order calls always generate from current evidence, so their output and volume may dominate the billed total. Keep two answers separate: the counterfactual says what safe reuse avoided; actual spend says what belongs on this release ledger.

05-price-a-traffic-replay.py
1@dataclass(frozen=True) 2class TrafficSlice: 3 outcome: RequestOutcome 4 requests: int 5 6daily_replay = [ 7 TrafficSlice(semantic_hit, requests=3_200), 8 TrafficSlice(prefix_hit, requests=1_800), 9 TrafficSlice( 10 RequestOutcome( 11 feature="live-order-answer", 12 decision="GENERATE_LIVE_DATA", 13 usage=Usage(input_tokens=900, cached_input_tokens=0, output_tokens=95), 14 ), 15 requests=3_000, 16 ), 17 TrafficSlice( 18 RequestOutcome( 19 feature="access-exception-answer", 20 decision="GENERATE_PREFIX_HIT", 21 usage=Usage(input_tokens=2_200, cached_input_tokens=1_280, output_tokens=220), 22 ), 23 requests=500, 24 ), 25] 26 27actual_daily_spend = sum( 28 generated_cost(slice_.outcome) * slice_.requests 29 for slice_ in daily_replay 30) 31avoided_daily_generation = sum( 32 price_usage(slice_.outcome.counterfactual_usage).total_usd * slice_.requests 33 for slice_ in daily_replay 34 if slice_.outcome.counterfactual_usage is not None 35) 36 37assert actual_daily_spend == Decimal("21.761000") 38assert avoided_daily_generation == Decimal("13.824000") 39 40print(f"generated_daily_spend=${dollars(actual_daily_spend)}") 41print(f"semantic_hit_avoided_daily=${dollars(avoided_daily_generation)}") 42print(f"request_count={sum(slice_.requests for slice_ in daily_replay)}")
Output
1generated_daily_spend=$21.76 2semantic_hit_avoided_daily=$13.82 3request_count=8500

The replay reports $21.76 of generated spend and $13.82 of avoided generation. Avoided generation is evidence, not an invoice credit, so keep it out of actual spend. If an answer hit later fails review, its quality failure matters before its apparent savings.

Reduce output only under an answer contract

Output reductions can matter when output tokens carry the highest rate. Blind truncation isn't a cost strategy. PolicyOps's cited support answer must state eligibility, cite policy evidence, and name the requester's next step. Compare three formats generated from the same input:

CandidateIntended changeSafe for automatic support?
VerboseFull explanation with repetitionCorrect but unnecessarily long
Concise citedOne decision, one citation, one next stepCandidate for release
TruncatedHard stop before citation and actionReject

The contract is three booleans, not a token target. Predict the selection before running the cell: the shortest candidate that keeps all three fields should win, while a 55-token answer that drops evidence must be rejected.

06-optimize-output-under-contract.py
1@dataclass(frozen=True) 2class AnswerCandidate: 3 label: str 4 output_tokens: int 5 states_decision: bool 6 cites_policy: bool 7 gives_next_step: bool 8 9def satisfies_answer_contract(candidate: AnswerCandidate) -> bool: 10 return ( 11 candidate.states_decision 12 and candidate.cites_policy 13 and candidate.gives_next_step 14 ) 15 16candidates = [ 17 AnswerCandidate("verbose", 220, True, True, True), 18 AnswerCandidate("concise_cited", 130, True, True, True), 19 AnswerCandidate("truncated", 55, True, False, False), 20] 21safe_candidates = [candidate for candidate in candidates if satisfies_answer_contract(candidate)] 22selected_answer = min(safe_candidates, key=lambda candidate: candidate.output_tokens) 23 24verbose_usage = Usage(input_tokens=2_200, cached_input_tokens=1_280, output_tokens=220) 25concise_usage = Usage(input_tokens=2_200, cached_input_tokens=1_280, output_tokens=selected_answer.output_tokens) 26saved_per_exception = price_usage(verbose_usage).total_usd - price_usage(concise_usage).total_usd 27 28assert selected_answer.label == "concise_cited" 29assert not satisfies_answer_contract(candidates[-1]) 30assert saved_per_exception == Decimal("0.001350") 31 32print(f"selected={selected_answer.label}") 33print(f"rejected={candidates[-1].label}") 34print(f"saved_per_exception=${saved_per_exception:.6f}") 35print(f"saved_per_replay_day=${dollars(saved_per_exception * 500)}")
Output
1selected=concise_cited 2rejected=truncated 3saved_per_exception=$0.001350 4saved_per_replay_day=$0.68

The cheapest candidate is rejected. The concise form saves $0.001350 per exception, or $0.68 across this 500-request slice, while preserving the contract. A compliant shorter answer may also reduce decode time, but verify p95 latency and retry rate instead of inferring either from token count alone.

Batch only work that can wait

Batch is a scheduling choice before it's a pricing choice. OpenAI documents an asynchronous path with 50% lower costs than synchronous APIs and completion within 24 hours.[3]Reference 3OpenAI Batch API Guidehttps://developers.openai.com/api/docs/guides/batch A live customer can't accept that latency contract, but a nightly regression run can.

The pricing page publishes Batch token rates explicitly, so the ledger reads that row instead of deriving every category from one multiplier.[1]Reference 1OpenAI API Pricinghttps://developers.openai.com/api/docs/pricing In this fixture, the displayed $0.13 cached-input rate means the replay total isn't the exact result of halving every Standard-row decimal locally. Predict the eligibility flags before running the cell: live support stays Standard; nightly evaluation can use Batch.

07-separate-interactive-and-batch-work.py
1@dataclass(frozen=True) 2class Workload: 3 name: str 4 interactive: bool 5 deadline_hours: int 6 endpoint_supported: bool 7 daily_requests: int 8 usage: Usage 9 10def batch_eligible(workload: Workload) -> bool: 11 return ( 12 not workload.interactive 13 and workload.deadline_hours >= 24 14 and workload.endpoint_supported 15 ) 16 17live_support = Workload( 18 name="live-support-answer", 19 interactive=True, 20 deadline_hours=0, 21 endpoint_supported=True, 22 daily_requests=3_000, 23 usage=Usage(input_tokens=900, cached_input_tokens=0, output_tokens=95), 24) 25nightly_eval = Workload( 26 name="nightly-release-eval", 27 interactive=False, 28 deadline_hours=24, 29 endpoint_supported=True, 30 daily_requests=2_000, 31 usage=Usage(input_tokens=1_800, cached_input_tokens=1_280, output_tokens=80), 32) 33 34nightly_sync_cost = price_usage(nightly_eval.usage).total_usd * nightly_eval.daily_requests 35nightly_batch_cost = price_usage(nightly_eval.usage, batch=True).total_usd * nightly_eval.daily_requests 36 37assert not batch_eligible(live_support) 38assert batch_eligible(nightly_eval) 39assert nightly_batch_cost == Decimal("2.832800") 40assert nightly_batch_cost < nightly_sync_cost 41 42print(f"live_support_batchable={batch_eligible(live_support)}") 43print(f"nightly_eval_batchable={batch_eligible(nightly_eval)}") 44print(f"nightly_eval_sync=${dollars(nightly_sync_cost)}") 45print(f"nightly_eval_batch=${dollars(nightly_batch_cost)}")
Output
1live_support_batchable=False 2nightly_eval_batchable=True 3nightly_eval_sync=$5.64 4nightly_eval_batch=$2.83

Your nightly evaluation workload is cheaper through Batch. Should the interactive support endpoint use the same path?

Answer

No. The Batch completion window changes latency semantics. Only work that can wait belongs on that path.

Attribute cost to decisions, not models alone

An invoice tells you how much you spent. A release trace tells you why. Each row should name the evaluated release, feature, decision, pricing mode, rate-card identity, token usage, and output-contract evidence.

Recording only a model name can't distinguish an unsafe answer-cache promotion from a harmless increase in live-order traffic. Make contract evidence and rate-card identity mandatory instead of silently repricing old usage with whichever table happens to be loaded.

Sort billed dollars by feature before changing prompts. In this fixture, live-order answers own $11.03 of $23.92, or 46.1%. If a feature crosses a team-defined triage threshold, such as 40% for this release, inspect its volume, output tokens, retries, and tool path first. That threshold prioritizes investigation; it doesn't prove the feature is inefficient.

08-build-feature-cost-telemetry.py
1@dataclass(frozen=True) 2class LedgerTrace: 3 release_id: str 4 feature: str 5 decision: str 6 requests: int 7 usage: Usage | None 8 rate_card_id: str 9 contract_passed: bool 10 contract_evidence_id: str 11 batch: bool = False 12 13def trace_unit_spend(trace: LedgerTrace, card: RateCard = rate_card) -> Decimal: 14 if trace.rate_card_id != card.rate_card_id: 15 raise ValueError("trace rate card does not match pricing table") 16 if trace.usage is None: 17 return Decimal("0") 18 return price_usage(trace.usage, card, batch=trace.batch).total_usd 19 20def trace_spend(trace: LedgerTrace, card: RateCard = rate_card) -> Decimal: 21 return trace_unit_spend(trace, card) * trace.requests 22 23release_id = "support-release-2026-05-cost-v1" 24optimized_replay = [ 25 TrafficSlice( 26 RequestOutcome( 27 feature=item.outcome.feature, 28 decision=item.outcome.decision, 29 usage=concise_usage if item.outcome.feature == "access-exception-answer" else item.outcome.usage, 30 counterfactual_usage=item.outcome.counterfactual_usage, 31 ), 32 requests=item.requests, 33 ) 34 for item in daily_replay 35] 36traces = [ 37 LedgerTrace( 38 release_id=release_id, 39 feature=item.outcome.feature, 40 decision=item.outcome.decision, 41 requests=item.requests, 42 usage=item.outcome.usage, 43 rate_card_id=rate_card.rate_card_id, 44 contract_passed=True, 45 contract_evidence_id=( 46 "approved-public-policy-cache@v1" 47 if item.outcome.decision == "SEMANTIC_ANSWER_HIT" 48 else "cited-support-answer-v3@canary" 49 ), 50 ) 51 for item in optimized_replay 52] 53traces.append( 54 LedgerTrace( 55 release_id=release_id, 56 feature=nightly_eval.name, 57 decision="BATCH_OFFLINE_EVAL", 58 requests=nightly_eval.daily_requests, 59 usage=nightly_eval.usage, 60 rate_card_id=rate_card.rate_card_id, 61 contract_passed=True, 62 contract_evidence_id="nightly-release-eval@batch-eligible", 63 batch=True, 64 ) 65) 66 67spend_by_feature: defaultdict[str, Decimal] = defaultdict(lambda: Decimal("0")) 68for trace in traces: 69 spend_by_feature[trace.feature] += trace_spend(trace) 70 71total_with_eval = sum(spend_by_feature.values()) 72trace_contracts_complete = all( 73 trace.contract_passed 74 and trace.contract_evidence_id 75 and trace.rate_card_id == rate_card.rate_card_id 76 for trace in traces 77) 78stale_trace = replace(traces[0], rate_card_id="openai-gpt-5.4-short-context-2026-04-30") 79assert spend_by_feature["public-policy-answer"] == Decimal("7.776000") 80assert actual_daily_spend - sum(trace_spend(trace) for trace in traces[:-1]) == Decimal("0.675000") 81assert trace_contracts_complete 82 83for feature in sorted(spend_by_feature): 84 print(f"{feature}=${dollars(spend_by_feature[feature])}") 85print(f"daily_total_with_eval=${dollars(total_with_eval)}") 86print(f"trace_contracts_complete={trace_contracts_complete}") 87try: 88 trace_spend(stale_trace) 89except ValueError as error: 90 print(f"stale_rate_card_rejected={error}")
Output
1access-exception-answer=$2.29 2live-order-answer=$11.03 3nightly-release-eval=$2.83 4public-policy-answer=$7.78 5daily_total_with_eval=$23.92 6trace_contracts_complete=True 7stale_rate_card_rejected=trace rate card does not match pricing table
Ranked daily spend for support-release-2026-05-cost-v1. Live-order answers are $11.03 (46.1 percent), generated public-policy answers $7.78 (32.5 percent), nightly eval $2.83 (11.8 percent), and access-exception answers $2.29 (9.6 percent). The $23.92 total is actual billed spend, including batch eval, not avoided semantic-cache savings.
Rank billed dollars, not avoided cache savings. Live-order generation is 46% of the $23.92 day, so that's the first lane to inspect when the forecast gets tight.

Forecast and gate the release

A release gate answers two separate questions: can we afford this traffic, and can we trust the answers? A cost target alone rewards short, wrong responses. A quality target alone leaves no capacity or margin plan. Require both.

The canary report evaluates the concise cited-answer policy for access exceptions. It projects the replay mix across 30 days and includes the nightly offline evaluation run. Before running it, predict the result: $23.9188 per day becomes a $717.56 monthly forecast, and promotion still requires the quality checks to pass.

09-evaluate-a-release-budget.py
1@dataclass(frozen=True) 2class QualityReport: 3 cited_answer_pass_rate: Decimal 4 unsafe_cache_hits: int 5 evaluated_answers: int 6 7@dataclass(frozen=True) 8class PromotionPolicy: 9 monthly_budget_usd: Decimal 10 minimum_cited_answer_pass_rate: Decimal 11 maximum_unsafe_cache_hits: int 12 minimum_evaluated_answers: int 13 14quality = QualityReport( 15 cited_answer_pass_rate=Decimal("0.997"), 16 unsafe_cache_hits=0, 17 evaluated_answers=2_000, 18) 19promotion_policy = PromotionPolicy( 20 monthly_budget_usd=Decimal("750.00"), 21 minimum_cited_answer_pass_rate=Decimal("0.995"), 22 maximum_unsafe_cache_hits=0, 23 minimum_evaluated_answers=2_000, 24) 25monthly_forecast = total_with_eval * Decimal("30") 26budget_headroom = promotion_policy.monthly_budget_usd - monthly_forecast 27uniform_volume_growth_headroom = budget_headroom / monthly_forecast 28 29sample_size_passed = ( 30 quality.evaluated_answers >= promotion_policy.minimum_evaluated_answers 31) 32quality_passed = ( 33 trace_contracts_complete 34 and sample_size_passed 35 and quality.cited_answer_pass_rate >= promotion_policy.minimum_cited_answer_pass_rate 36 and quality.unsafe_cache_hits <= promotion_policy.maximum_unsafe_cache_hits 37) 38budget_passed = monthly_forecast <= promotion_policy.monthly_budget_usd 39 40assert dollars(monthly_forecast) == Decimal("717.56") 41assert dollars(budget_headroom) == Decimal("32.44") 42assert quality_passed and budget_passed 43 44print(f"monthly_forecast=${dollars(monthly_forecast)}") 45print(f"budget_headroom=${dollars(budget_headroom)}") 46print(f"uniform_volume_growth_headroom={uniform_volume_growth_headroom:.1%}") 47print(f"sample_size_passed={sample_size_passed}") 48print(f"quality_passed={quality_passed}") 49print(f"budget_passed={budget_passed}")
Output
1monthly_forecast=$717.56 2budget_headroom=$32.44 3uniform_volume_growth_headroom=4.5% 4sample_size_passed=True 5quality_passed=True 6budget_passed=True

This forecast repeats one measured replay day 30 times. The $32.44 remainder represents only 4.5% uniform growth, so replay low, expected, and high volume scenarios before ramping traffic. The hard budget gate passes, but a team could add a soft alert at 90% budget consumption; this fixture is already at 95.7%, so it deserves an owner and a volume plan even with budget_passed=True.

Release gate for support-release-2026-05-cost-v1. The $750 monthly budget is $717.56 forecast spend plus $32.44 headroom, so 4.5 percent uniform growth fills the remainder. Quality still passes: cited-answer rate 99.7 percent over a 99.5 percent floor, zero unsafe cache hits, and 2,000 evaluated answers. Promotion requires both receipts.
The stacked $750 bar makes the remainder visible: $32.44 is only 4.5% uniform growth. Quality also clears, so the cost policy can promote, but volume scenarios still need replay.

Two routes have the same cost per attempt, but one often needs a retry before producing a valid answer. Which cost should the release decision compare?

Answer

Compare cost per successful answer or task, including retries and failed attempts. Cost per request would hide the extra spend caused by lower reliability.

Produce the next policy input

The next lesson implements model gateway routing and fallbacks. Hand it a budget contract, not a half-built router inside cost analysis. The contract says what every future route must preserve: citation schema, an approved generated-answer ceiling, and the release evidence behind that limit.

Set the ceiling as a policy value that the observed maximum fits under. Treat it as a lane-admission limit for one successful generation, not a bound on total request spend. A failed primary can bill before timeout, and a contract-preserving fallback can bill again. Incident TCO can exceed maximum_generated_answer_usd when attempts stack, so the gateway lesson must track request-level spend separately.

Before running the final cell, predict the handoff: the observed maximum is $0.004570, the policy ceiling is $0.005000, and the status can promote only because quality and budget gates already passed.

10-emit-the-budget-contract.py
1@dataclass(frozen=True) 2class GatewayBudgetContract: 3 release_id: str 4 required_answer_schema: str 5 maximum_generated_answer_usd: Decimal 6 monthly_forecast_usd: Decimal 7 rate_card_id: str 8 status: str 9 10observed_max_generated_cost = max( 11 trace_unit_spend(trace) 12 for trace in traces 13 if trace.usage is not None and not trace.batch 14) 15per_answer_budget_usd = Decimal("0.005000") 16assert observed_max_generated_cost <= per_answer_budget_usd 17 18status = "PROMOTE_COST_POLICY" if quality_passed and budget_passed else "HOLD_RELEASE" 19gateway_contract = GatewayBudgetContract( 20 release_id=release_id, 21 required_answer_schema="cited-support-answer-v3", 22 maximum_generated_answer_usd=per_answer_budget_usd, 23 monthly_forecast_usd=dollars(monthly_forecast), 24 rate_card_id=rate_card.rate_card_id, 25 status=status, 26) 27 28assert gateway_contract.status == "PROMOTE_COST_POLICY" 29assert observed_max_generated_cost == Decimal("0.004570") 30assert gateway_contract.maximum_generated_answer_usd == Decimal("0.005000") 31 32print(f"status={gateway_contract.status}") 33print(f"required_schema={gateway_contract.required_answer_schema}") 34print(f"observed_max_generated_answer=${observed_max_generated_cost:.6f}") 35print(f"max_generated_answer=${gateway_contract.maximum_generated_answer_usd:.6f}") 36print("next_step=apply_contract_in_gateway_routing")
Output
1status=PROMOTE_COST_POLICY 2required_schema=cited-support-answer-v3 3observed_max_generated_answer=$0.004570 4max_generated_answer=$0.005000 5next_step=apply_contract_in_gateway_routing

What the ledger proves

By now, "cache hit" isn't an explanation by itself. Each claim about this release needs the receipt that supports it:

EvidenceCorrect conclusion
Semantic answer hit with valid release scopeNew generation was avoided for an already approved answer class
Provider cached_tokens on a generated answerRepeated prefix processing was charged at the cached-input rate
Concise cited-answer candidate passes evaluationOutput reduction is eligible for release
Nightly evaluation classified as noninteractiveBatch discount can be modeled without changing customer latency
Every usage trace pins the loaded rate-card identityHistorical spend remains reproducible instead of being silently repriced
Monthly forecast and quality check passCost policy can be promoted and handed to gateway design

Wait for usage traces before claiming a prompt rewrite improved pricing through cached tokens. Count semantic-cache savings only while accepted-hit quality remains valid. Never approve a shorter output merely because it costs less.

Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.A generated answer reports 1,800 input tokens, 1,280 cached input tokens, and 180 output tokens. Standard rates are USD 2.50 per million fresh input, USD 0.25 per million cached input, and USD 15.00 per million output. What total belongs in the ledger?

Correct answer: USD 0.004320, from 520 fresh input tokens, 1,280 cached input tokens, and 180 output tokens.

Fresh input is 1,800 minus 1,280, or 520 tokens. The three charges are 0.001300, 0.000320, and 0.002700, which sum to USD 0.004320. Prefix reuse discounts input work but doesn't remove generation.

2.A provider trace reports output_tokens=1,186 and reasoning_tokens=1,024 for a GPT-5.4 response. Which output count should pricing use, and what happens if generation ended while all output tokens were reasoning?

Correct answer: Price 1,186 output tokens once; reasoning_tokens explains part of that total, including an incomplete call.

Provider output_tokens already includes hidden reasoning tokens. The detail field is diagnostic, not an extra billable bucket. A max-token stop can bill 1,024 reasoning-only output tokens even when the user sees no completed answer.

3.A replay has 3,200 semantic answer-cache hits. No model call runs for those requests, and the counterfactual generated answer would cost USD 0.004320 with a prefix hit. What should the ledger report?

Correct answer: USD 0 actual generated spend and USD 13.824 avoided generation, because stored answers skipped model calls.

A semantic answer hit returns an already evaluated response, so actual generated-token spend is zero. The counterfactual is reported separately: 3,200 times USD 0.004320 equals USD 13.824. Avoided spend isn't an invoice credit.

4.A GPT-5.4 trace contains 300,000 input tokens. The short-context row is USD 2.50, USD 0.25, and USD 15.00 per million tokens; the long-context row is USD 5.00, USD 0.50, and USD 22.50. How should the call be priced?

Correct answer: Use the long-context row for the full session, because exceeding 272K reprices the whole call.

The long-context rule applies to sessions above the input threshold and reprices the full session, not just overflow tokens. The row selection must therefore happen before splitting fresh, cached, and output categories.

5.An answer contract requires a decision, policy citation, and next step. Verbose has all three at 220 output tokens, concise_cited has all three at 130, and truncated has only the decision at 55. Which candidate can support a cost-saving release?

Correct answer: concise_cited, because it reduces output while preserving every required contract field.

The truncated form is cheaper but fails the citation and next-step requirements. Cost savings count only after the answer contract still passes. Concise_cited is the shortest compliant format.

6.Live support has an interactive deadline of zero hours. Nightly release evaluation is noninteractive, has a 24-hour deadline, and uses an endpoint that supports Batch. The rate card publishes a Batch cached-input row at USD 0.13 per million. Which pricing path is valid?

Correct answer: Use Standard for live support and the published Batch rows for nightly evaluation.

Interactive support can't accept the asynchronous completion window, so it stays on Standard. Nightly evaluation can wait and uses the explicit Batch table. A local half-price calculation would lose provider-published rounding and row differences.

7.A historical usage trace pins rate_card_id openai-gpt-5.4-short-context-2026-05-31, but the currently loaded table has openai-gpt-5.4-short-context-2026-06-30. Token counts and model family match. What should happen before historical spend is computed?

Correct answer: Reject the mismatch or require an explicit repricing path tied to the trace's recorded rate card.

Pricing rows can change while the model family stays the same. Applying whichever table happens to be loaded would make historical spend irreproducible. The ledger needs the pinned card or an explicit, visible repricing decision.

8.A canary's daily spend including nightly evaluation is USD 23.9188, so the 30-day forecast rounds to USD 717.56 against a USD 750 budget. Cited-answer pass rate is 0.997, but unsafe cache hits equal 1 while policy allows 0. What is the decision?

Correct answer: Hold the release because the budget passes but the unsafe-cache quality gate fails.

The forecast passes, and pass rate exceeds its floor, but one unsafe cache hit violates a maximum of zero. Promotion requires both quality and budget evidence. Avoided generation isn't subtracted from actual billed spend.

9.Two model routes have the same cost per attempt, but one often retries before producing a valid cited answer. Which comparison exposes its real economic cost?

Correct answer: Compare cost per successful answer, including failed attempts and retries, rather than cost per request.

A route that retries spends more before delivering one acceptable result. Cost per request or first attempt hides that reliability tax. Cost per successful answer aligns spend with the unit the product actually delivers.

10.The next gateway needs cost policy, but routing and fallback code don't exist yet. The observed maximum single generated answer is USD 0.004570, while retries can stack multiple attempts. Which artifact should the ledger emit?

Correct answer: A budget contract with answer schema, approved per-answer ceiling, monthly forecast, rate_card_id, status, and separate request-level spend accounting.

The ledger hands routing an admission contract, not an unfinished router. The per-answer ceiling is a policy limit that observed successful generations fit under, while retries can make request TCO larger and must be tracked separately.

10 questions remaining.

Next Step
Continue to Model Gateways, Routing, and Fallbacks

You now have a pinned budget contract: schema `cited-support-answer-v3`, a `$0.005000` per-answer admission ceiling, and `PROMOTE_COST_POLICY`. Next you'll use that contract to filter lanes and fallbacks without dropping citations, privacy, or review when a cheaper path appears.

PreviousSemantic Caching & Cost Optimization
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

OpenAI API Pricing

OpenAI · 2026

https://developers.openai.com/api/docs/pricing

GPT-5.4 Model

OpenAI · 2026

https://developers.openai.com/api/docs/models/gpt-5.4

OpenAI Batch API Guide

OpenAI · 2026

https://developers.openai.com/api/docs/guides/batch

Reasoning models

OpenAI · 2026

https://developers.openai.com/api/docs/guides/reasoning

Efficient Memory Management for Large Language Model Serving with PagedAttention.

Kwon, W., et al. · 2023 · SOSP 2023

https://arxiv.org/abs/2309.06180

Prompt caching

OpenAI · 2026

https://developers.openai.com/api/docs/guides/prompt-caching

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.