{"slug": "adaptive-thinking-killed-my-token-budget-code-migrating-off-budget-tokens", "title": "Adaptive Thinking Killed My Token Budget Code: Migrating Off budget_tokens", "summary": "An engineer migrating from Claude Opus 4.5 to Opus 4.8 found that the fixed `budget_tokens` parameter for thinking has been replaced by adaptive thinking with an `effort` knob. The developer adapted by removing token budget calculations and instead selecting effort levels per workload, discovering that higher effort often reduced total cost on agentic tasks. The migration also required handling new defaults for streaming thinking blocks and removing deprecated parameters like temperature.", "body_md": "I had a tidy little helper that computed a thinking budget based on input size. Something like \"give the model 30% of the context as thinking room.\" It worked great on Opus 4.5. Then I tried to point it at Opus 4.8 and got a 400. The whole concept I had built around is gone in the current models. Here is what replaced it and how I migrated.\n\nThe old pattern looked like this:\n\n``` js\n// Opus 4.5 and earlier\nconst response = await client.messages.create({\n  model: \"claude-opus-4-5\",\n  max_tokens: 16000,\n  thinking: { type: \"enabled\", budget_tokens: 8000 },\n  messages,\n});\n```\n\nOn Opus 4.7, 4.8, and Fable 5, `thinking: { type: \"enabled\", budget_tokens: N }`\n\nreturns a 400. The fixed token budget is dead. The replacement is adaptive thinking, where the model decides how much to think, plus an `effort`\n\nknob that controls overall token spend.\n\n``` js\n// Opus 4.8\nconst response = await client.messages.create({\n  model: \"claude-opus-4-8\",\n  max_tokens: 16000,\n  thinking: { type: \"adaptive\" },\n  output_config: { effort: \"high\" }, // low | medium | high | xhigh | max\n  messages,\n});\n```\n\nMy old budget code was a guess dressed up as a calculation. I had no real basis for \"30% of context.\" I picked it because it felt reasonable and the outputs looked fine. Adaptive thinking moves that decision to the model, which sees the actual problem.\n\nThe mental model shift: `budget_tokens`\n\ncontrolled how much the model could think. `effort`\n\ncontrols how much it thinks *and acts*. They are not the same axis, so there is no clean 1:1 mapping. I stopped trying to translate \"8000 tokens\" into an effort level and instead picked based on the workload.\n\nAfter running my own evals, here is where I landed:\n\n| Workload | Effort | Notes |\n|---|---|---|\n| Classification, routing | `low` |\nFast, scoped, not intelligence-sensitive |\n| Most app traffic |\n`medium` to `high`\n|\nThe balance point |\n| Coding and agentic loops | `xhigh` |\nBest for these; it is the Claude Code default |\n| Correctness-critical, latency-insensitive | `max` |\nWhen being wrong costs more than tokens |\n\nOne thing that surprised me: higher effort up front often *reduced* total cost on agentic work because the model planned better and took fewer turns. I had assumed `max`\n\nalways meant more tokens. On multi-step tasks, it sometimes meant fewer.\n\n`budget_tokens`\n\nacross the codebase.`thinking: { type: \"enabled\", budget_tokens: N }`\n\nwith `thinking: { type: \"adaptive\" }`\n\n.`output_config: { effort: \"...\" }`\n\nand pick a level per call site, not one global value.`temperature`\n\n/ `top_p`\n\n/ `top_k`\n\nparams (those also 400 on 4.7+).`response.model`\n\n.\n\n``` js\nconst r = await client.messages.create({\n  model: \"claude-opus-4-8\",\n  max_tokens: 64,\n  thinking: { type: \"adaptive\" },\n  messages: [{ role: \"user\", content: \"ping\" }],\n});\nconsole.assert(r.model.startsWith(\"claude-opus-4-8\"), r.model);\n```\n\nOn Opus 4.7+ and Fable 5, thinking blocks still stream but their text is empty by default. If you were rendering reasoning to a UI, you now see a long pause instead of progress. Opt back in:\n\n```\nthinking: { type: \"adaptive\", display: \"summarized\" }\n```\n\nI missed this for an afternoon and thought streaming was broken. It was just the new default (`omitted`\n\n).\n\nI built abstraction on top of a parameter that the platform later removed. That is the risk of wrapping a vendor knob in your own logic before you understand whether the knob is fundamental or incidental. `budget_tokens`\n\nwas incidental. The fundamental thing was \"let the model think when it helps,\" and adaptive thinking expresses that directly. Less of my code, more of theirs, and the outputs got better. I will take that trade.", "url": "https://wpnews.pro/news/adaptive-thinking-killed-my-token-budget-code-migrating-off-budget-tokens", "canonical_source": "https://dev.to/pavelespitia/adaptive-thinking-killed-my-token-budget-code-migrating-off-budgettokens-2f49", "published_at": "2026-07-14 15:36:52+00:00", "updated_at": "2026-07-14 15:59:01.856104+00:00", "lang": "en", "topics": ["large-language-models", "developer-tools", "ai-products"], "entities": ["Claude Opus 4.5", "Claude Opus 4.8", "Claude Opus 4.7", "Fable 5", "Claude Code"], "alternates": {"html": "https://wpnews.pro/news/adaptive-thinking-killed-my-token-budget-code-migrating-off-budget-tokens", "markdown": "https://wpnews.pro/news/adaptive-thinking-killed-my-token-budget-code-migrating-off-budget-tokens.md", "text": "https://wpnews.pro/news/adaptive-thinking-killed-my-token-budget-code-migrating-off-budget-tokens.txt", "jsonld": "https://wpnews.pro/news/adaptive-thinking-killed-my-token-budget-code-migrating-off-budget-tokens.jsonld"}}