<?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: amdmsz</title>
    <description>The latest articles on DEV Community by amdmsz (@amdmsz).</description>
    <link>https://dev.to/amdmsz</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%2F4025558%2F57eb1f0a-6dc5-4258-81dd-3a96e5523efd.png</url>
      <title>DEV Community: amdmsz</title>
      <link>https://dev.to/amdmsz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amdmsz"/>
    <language>en</language>
    <item>
      <title>Your Codex model shuts off July 23 — a 7-day migration map</title>
      <dc:creator>amdmsz</dc:creator>
      <pubDate>Wed, 15 Jul 2026 21:19:25 +0000</pubDate>
      <link>https://dev.to/amdmsz/your-codex-model-shuts-off-july-23-a-7-day-migration-map-nm8</link>
      <guid>https://dev.to/amdmsz/your-codex-model-shuts-off-july-23-a-7-day-migration-map-nm8</guid>
      <description>&lt;p&gt;I pin model IDs on purpose. Floating aliases have burned me before — a silent swap under a &lt;code&gt;-latest&lt;/code&gt; tag once changed a tool-calling detail in production and cost me a Saturday with &lt;code&gt;git bisect&lt;/code&gt; and a coffee I didn't enjoy. So everything I run points at a dated snapshot. &lt;code&gt;gpt-5-codex&lt;/code&gt; was one of them.&lt;/p&gt;

&lt;p&gt;Last week I finally read OpenAI's deprecations page top to bottom instead of skimming it, and there it was: &lt;code&gt;gpt-5-codex&lt;/code&gt;, retiring &lt;strong&gt;2026-07-23&lt;/strong&gt;, sitting quietly next to ten of its neighbors. As I write this it's July 16. That's &lt;strong&gt;seven days&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you pinned any of the snapshots below, consider this your heads-up. Here's the full retirement list, how to find out in about ten minutes whether you're exposed, the replacement map with the things I'd actually regression-test, and the one structural change that turned my last forced migration from scary into boring.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shutdown list (retiring 2026-07-23)
&lt;/h2&gt;

&lt;p&gt;Straight from OpenAI's &lt;a href="https://developers.openai.com/api/docs/deprecations" rel="noopener noreferrer"&gt;API deprecations page&lt;/a&gt;. I checked each row against the source while writing this — do the same before you act on it, because dates and replacements do get revised.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Retiring model&lt;/th&gt;
&lt;th&gt;OpenAI's suggested replacement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-5-codex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-5.1-codex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-5.1-codex-max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-5.1-codex-mini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.4-mini&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-5.2-codex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-5-chat-latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-5.1-chat-latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-4o-search-preview-2025-03-11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.4-mini&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-4o-mini-search-preview-2025-03-11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.4-mini&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gpt-4o-mini-tts-2025-03-20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-4o-mini-tts-2025-12-15&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;computer-use-preview-2025-03-11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;computer-use-preview&lt;/code&gt; (or &lt;code&gt;gpt-5.4-mini&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note the shape of it: the &lt;code&gt;*-codex&lt;/code&gt; line collapses into &lt;code&gt;gpt-5.5&lt;/code&gt; — except &lt;code&gt;gpt-5.1-codex-mini&lt;/code&gt;, which drops to &lt;code&gt;gpt-5.4-mini&lt;/code&gt; — the &lt;code&gt;chat-latest&lt;/code&gt; aliases fold into &lt;code&gt;gpt-5.5&lt;/code&gt; too, the two &lt;code&gt;*-search-preview&lt;/code&gt; snapshots move to &lt;code&gt;gpt-5.4-mini&lt;/code&gt;, and the two preview families (&lt;code&gt;tts&lt;/code&gt;, &lt;code&gt;computer-use&lt;/code&gt;) just roll to a newer dated snapshot / the undated alias. Once you see the buckets, the migration is less intimidating than the eleven-row table looks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find out if you're exposed (about ten minutes)
&lt;/h2&gt;

&lt;p&gt;The trap isn't the model IDs you remember hardcoding. It's the ones you forgot — buried in an &lt;code&gt;.env&lt;/code&gt;, a config table, a feature flag, a notebook a teammate ran once. Two passes catch almost all of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Grep the code.&lt;/strong&gt; One regex covers every retiring ID:&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="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rniE&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'gpt-5-codex|gpt-5\.1-codex|gpt-5\.2-codex|gpt-5(\.1)?-chat-latest|gpt-4o(-mini)?-search-preview-2025-03-11|gpt-4o-mini-tts-2025-03-20|computer-use-preview-2025-03-11'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.py'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.ts'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.js'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.go'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.rb'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.java'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.env*'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.y*ml'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.json'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.toml'&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Grep what you actually shipped.&lt;/strong&gt; Code search misses IDs that arrive from a database, a dashboard toggle, or a user-supplied param. If you log outbound requests as JSON lines with a &lt;code&gt;.model&lt;/code&gt; field, count what really went over the wire:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'select(.model) | .model'&lt;/span&gt; requests.log &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'codex|chat-latest|search-preview-2025-03-11|gpt-4o-mini-tts-2025-03-20|computer-use-preview-2025-03-11'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No structured logs? OpenAI's usage export / dashboard breaks spend down by model — sort by name and look for the eleven above. If a retiring ID shows up there but not in your grep, that's exactly the config-driven caller you'd have missed.&lt;/p&gt;

&lt;p&gt;Keep the retiring set around as a constant; it's handy in a CI check that fails a build if any of these strings reappears:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;RETIRING_2026_07_23&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5-codex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.1-codex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.1-codex-max&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.1-codex-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.2-codex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5-chat-latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.1-chat-latest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-search-preview-2025-03-11&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini-search-preview-2025-03-11&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini-tts-2025-03-20&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;computer-use-preview-2025-03-11&lt;/span&gt;&lt;span class="sh"&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;h2&gt;
  
  
  The migration map — and what I'd actually test
&lt;/h2&gt;

&lt;p&gt;Swapping a string is the easy part. The replacements are not drop-in twins, so treat each bucket as a behavior change, not a rename.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;*-codex&lt;/code&gt; → &lt;code&gt;gpt-5.5&lt;/code&gt;.&lt;/strong&gt; This is the biggest jump. The codex snapshots were tuned for agentic coding; &lt;code&gt;gpt-5.5&lt;/code&gt; is a general model. Re-run your coding evals / golden transcripts and watch reasoning depth, patch/diff formatting, and tool-call arguments. If you parse the model's output with anything stricter than "trust me," diff old vs. new on a real sample before you flip prod.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gpt-5.1-codex-mini&lt;/code&gt; → &lt;code&gt;gpt-5.4-mini&lt;/code&gt;.&lt;/strong&gt; Different family, not just a smaller codex. Latency and tool-calling shape can differ from the mini you tuned prompts against — re-check timeouts and any few-shot examples that leaned on the old behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gpt-5(.1)-chat-latest&lt;/code&gt; → &lt;code&gt;gpt-5.5&lt;/code&gt;.&lt;/strong&gt; You were pinned to a moving alias, which is its own smell. Moving to a concrete model is an improvement; just confirm system-prompt adherence and output format on your top few flows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;*-search-preview-2025-03-11&lt;/code&gt; → &lt;code&gt;gpt-5.4-mini&lt;/code&gt;.&lt;/strong&gt; These preview models had search grounding baked in. A plain &lt;code&gt;gpt-5.4-mini&lt;/code&gt; may not ground the same way, so verify the capability you relied on still exists — and if you need live retrieval, wire up the web-search tool on the Responses API rather than assuming the base model does it for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gpt-4o-mini-tts-2025-03-20&lt;/code&gt; → &lt;code&gt;gpt-4o-mini-tts-2025-12-15&lt;/code&gt;.&lt;/strong&gt; Same family, newer snapshot — lowest risk. Still, spot-check voice, prosody, and any voice/format params you pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;computer-use-preview-2025-03-11&lt;/code&gt; → &lt;code&gt;computer-use-preview&lt;/code&gt;.&lt;/strong&gt; Dated snapshot to undated alias. Fine, but you're now back on a moving target; pin the new dated snapshot the moment one is published if you care about reproducibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My minimum regression checklist before flipping any of these in production:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the existing eval/golden set on the replacement and diff outputs, don't eyeball one prompt.&lt;/li&gt;
&lt;li&gt;Re-validate any parser or schema that consumes model output (tool-call JSON, code fences, function args).&lt;/li&gt;
&lt;li&gt;Sanity-check token accounting and cost on the new model so a "quick swap" doesn't quietly reprice a hot path.&lt;/li&gt;
&lt;li&gt;Confirm capability parity for anything special (search grounding, computer use, TTS voices) — replacements can drop or move a feature.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The next wave is already on the board (2026-12-11)
&lt;/h2&gt;

&lt;p&gt;While you're in the deprecations page, scroll down. A second batch — &lt;code&gt;gpt-5-2025-08-07&lt;/code&gt;, &lt;code&gt;gpt-5-mini&lt;/code&gt;, &lt;code&gt;gpt-5-nano&lt;/code&gt;, &lt;code&gt;gpt-5-pro&lt;/code&gt;, &lt;code&gt;o3&lt;/code&gt;, and &lt;code&gt;o3-pro&lt;/code&gt; — is slated to retire &lt;strong&gt;2026-12-11&lt;/strong&gt;, moving to &lt;code&gt;gpt-5.5&lt;/code&gt; / &lt;code&gt;gpt-5.4-mini&lt;/code&gt; / &lt;code&gt;gpt-5.4-nano&lt;/code&gt; / &lt;code&gt;gpt-5.5-pro&lt;/code&gt;. Not urgent this week, but if you're touching model config anyway, note it now so December isn't a second scramble.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pull model IDs into one layer, so the next one is boring
&lt;/h2&gt;

&lt;p&gt;Here's the part I keep relearning. The pain of a forced migration scales with &lt;strong&gt;how many places name the model&lt;/strong&gt;. When &lt;code&gt;gpt-5-codex&lt;/code&gt; is a literal string in nineteen files, three services, and a config table, a deprecation is a nineteen-file PR under a deadline. When the model name lives in exactly one routing/config layer, it's a one-line change and a redeploy.&lt;/p&gt;

&lt;p&gt;So my rule now: application code never spells a model ID. It asks for a &lt;em&gt;role&lt;/em&gt; — &lt;code&gt;"code-agent"&lt;/code&gt;, &lt;code&gt;"cheap-classifier"&lt;/code&gt;, &lt;code&gt;"tts"&lt;/code&gt; — and a small config maps that role to the concrete snapshot. A forced migration becomes editing that map, re-running evals, shipping. The eleven models above would have been one commit.&lt;/p&gt;

&lt;p&gt;In my own setup that routing layer is &lt;a href="https://docs.byesu.com/en/guide/openai-codex-migration-2026" rel="noopener noreferrer"&gt;byesu&lt;/a&gt;, an OpenAI-compatible gateway where a single key fronts &lt;code&gt;gpt-5.5&lt;/code&gt;/&lt;code&gt;gpt-5.6&lt;/code&gt; alongside Claude, Gemini, and Grok. Because it's OpenAI-compatible, the replacement models in that table are reachable by pointing my existing client at one base URL and changing the model string in one place — the SDK, request shape, and parsing code don't move. You don't need a gateway to get this benefit, to be clear; a plain constants file or a &lt;code&gt;MODEL_ROLES&lt;/code&gt; dict buys you most of it. The point is the indirection, not the tool. But having every provider behind one compatible endpoint is what let me treat "gpt-5-codex is gone" as a config edit instead of a cross-repo hunt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seven days
&lt;/h2&gt;

&lt;p&gt;Concretely, before July 23: run the two greps, list your exposed snapshots, pick the replacement per the table, diff outputs on real traffic, and ship. If you've got the eleven strings scattered across the codebase, this is also the cheap moment to pull them behind one role→model map — because December's batch is already scheduled, and there will always be another one after that.&lt;/p&gt;

&lt;p&gt;Go read the &lt;a href="https://developers.openai.com/api/docs/deprecations" rel="noopener noreferrer"&gt;source page&lt;/a&gt; yourself and check the dates against your own stack. Deprecation tables are the one kind of doc where "I'll get to it next sprint" has a hard cutoff.&lt;/p&gt;

&lt;p&gt;If you pinned &lt;code&gt;gpt-5-codex&lt;/code&gt; like I did — the clock's already running.&lt;/p&gt;

</description>
      <category>openai</category>
      <category>codex</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My "prompt caching" change made the bill go up — here's what I got wrong</title>
      <dc:creator>amdmsz</dc:creator>
      <pubDate>Wed, 15 Jul 2026 02:02:56 +0000</pubDate>
      <link>https://dev.to/amdmsz/my-prompt-caching-change-made-the-bill-go-up-heres-what-i-got-wrong-1k3a</link>
      <guid>https://dev.to/amdmsz/my-prompt-caching-change-made-the-bill-go-up-heres-what-i-got-wrong-1k3a</guid>
      <description>&lt;p&gt;I turned on prompt caching for an agent I run all day, watched the next invoice, and the number went &lt;strong&gt;up&lt;/strong&gt;. Not by a rounding error — meaningfully up. This is the write-up of why, because the thing I got wrong is the thing almost every "just add caching" post skips.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-sentence version of caching
&lt;/h2&gt;

&lt;p&gt;You send the same long system prompt (plus tool schemas, plus a pasted document) on every call. Without caching you pay full input price to make the model re-read that identical block every single time. Caching stores a &lt;strong&gt;prefix&lt;/strong&gt; of your prompt and, on the next call, charges a fraction to read it back instead of full price.&lt;/p&gt;

&lt;p&gt;Sounds free. It is not free. That's the trap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that bit me: writes cost &lt;em&gt;more&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;A cache entry has &lt;strong&gt;two&lt;/strong&gt; prices, and I only knew about one of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache read (a hit):&lt;/strong&gt; billed at as low as &lt;strong&gt;0.1x&lt;/strong&gt; the input rate. This is the number everyone quotes. ~90% off the cached tokens. Great.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache write:&lt;/strong&gt; billed at a &lt;strong&gt;premium&lt;/strong&gt; — around &lt;strong&gt;1.25x&lt;/strong&gt; the input rate on the short (5-minute) window. This is the number nobody mentions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the first time you send a prefix — or any time it's expired — you don't save 90%, you pay a 25% &lt;em&gt;surcharge&lt;/em&gt; to store it. Caching only comes out ahead once that same prefix is &lt;strong&gt;read back, not just written&lt;/strong&gt;. The good news: the write costs ~0.25x more than a normal read (1.25x vs 1x), and a single cache hit saves 0.9x (0.1x vs 1x) — so the &lt;em&gt;very first reuse&lt;/em&gt; already earns the premium back, and every hit after that is ~90% off. The catch is you have to actually get the hits.&lt;/p&gt;

&lt;p&gt;Which means the failure mode is specific and nasty: &lt;strong&gt;write a cache entry, then never read it.&lt;/strong&gt; You paid extra for storage you never used. Do that on every single call and you've built a machine that pays a surcharge to cache things it immediately throws away.&lt;/p&gt;

&lt;p&gt;That was exactly my bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually did wrong
&lt;/h2&gt;

&lt;p&gt;My "stable" system prefix wasn't stable. Buried near the top of it was this, which I'd added months earlier and forgotten:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current session started: 2026-07-14T09:41:07Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A timestamp. Regenerated every call. Which meant the prefix was &lt;strong&gt;different every call&lt;/strong&gt;, so the provider dutifully wrote a fresh cache entry every single time — full write premium — and never once got to read one back, because no two prefixes matched. I had turned on the expensive half of caching and none of the cheap half.&lt;/p&gt;

&lt;p&gt;The tell was right there in the response the whole time, I just wasn't looking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# cache_creation_input_tokens: 3948   &amp;lt;- writing every call
# cache_read_input_tokens: 0          &amp;lt;- never reading. there's your bug.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cache_creation&lt;/code&gt; climbing while &lt;code&gt;cache_read&lt;/code&gt; sits at zero is the signature of a busted prefix. If you take one thing from this post: &lt;strong&gt;log those two fields and watch them.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix is discipline, not more caching
&lt;/h2&gt;

&lt;p&gt;Caching rewards a prefix that is &lt;strong&gt;byte-for-byte identical&lt;/strong&gt; across calls and sits &lt;strong&gt;before&lt;/strong&gt; anything that changes. Two rules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Static content first, dynamic content last.&lt;/strong&gt; System prompt, tool definitions, the big pasted document — all up front and unchanging. The user's actual turn, the timestamp, the per-request ID — all at the &lt;em&gt;end&lt;/em&gt;, after the cached region. Anything that varies must live &lt;em&gt;downstream&lt;/em&gt; of the cache breakpoint, never inside it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Keep the prefix stone-cold identical.&lt;/strong&gt; No timestamps, no reordered JSON keys, no "helpful" per-request injection into the system block. One moving character and the whole prefix misses.&lt;/p&gt;

&lt;p&gt;For Claude, you mark where the stable prefix ends with a breakpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LONG_STABLE_INSTRUCTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# no timestamps in here!
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cache_control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ephemeral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# cache everything up to this point
&lt;/span&gt;    &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;todays_question&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;  &lt;span class="c1"&gt;# the only thing that varies
&lt;/span&gt;    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1024&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;For OpenAI-family models it's automatic above ~1024 tokens — same discipline, no breakpoint: keep the static stuff at the front and let it match the longest common prefix. One update worth knowing (edited July 15): &lt;strong&gt;the billing changed with GPT-5.6.&lt;/strong&gt; On GPT-5.5 and earlier there's no write premium and a shallower read discount — caching is pure, smaller upside. From GPT-5.6 onward OpenAI moved to the Anthropic-style structure: &lt;strong&gt;1.25x cache writes, 90% off cache reads&lt;/strong&gt;, plus explicit cache breakpoints and a 30-minute minimum cache life. Which means the timestamp trap in this post now bites on OpenAI too. You'll see it land in &lt;code&gt;prompt_tokens_details.cached_tokens&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I moved the timestamp to the user message, left the system block untouched between calls, and re-ran. &lt;code&gt;cache_read_input_tokens&lt;/code&gt; lit up on the second call and the bill for that agent dropped hard — because it loops twenty-plus times over the same head, and now that head is written once and read twenty times instead of written twenty times and read never.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where caching actually pays (and where it doesn't)
&lt;/h2&gt;

&lt;p&gt;It's amortization, not magic. It wins when a &lt;strong&gt;big, identical prefix is read many times&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent / tool loops&lt;/strong&gt; — every step resends the same system prompt and tool schemas. Ideal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-doc Q&amp;amp;A&lt;/strong&gt; — paste the document once, ask ten questions against it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulk extraction&lt;/strong&gt; — fat instruction + few-shot block, only the input row changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does &lt;em&gt;nothing&lt;/em&gt; for one-shot calls where the whole prompt is different every time — there's no repeated prefix to amortize against. On Claude (and GPT-5.6+, which shares the 1.25x write structure), explicitly caching there just burns the write premium for no return; on GPT-5.5 and earlier the attempt is free, but it won't help either. Match the tool to the workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  One gateway gotcha, since I run through one
&lt;/h2&gt;

&lt;p&gt;I don't call the providers directly — I route through a gateway (I use &lt;a href="https://docs.byesu.com/en/guide/prompt-caching" rel="noopener noreferrer"&gt;byesu&lt;/a&gt;, pay-as-you-go across Claude/GPT/Grok on one key). That adds a failure mode worth naming: &lt;strong&gt;some aggregators quietly break caching.&lt;/strong&gt; If a proxy reorders your fields or strips &lt;code&gt;cache_control&lt;/code&gt; or injects a per-request header into the prompt body, the prefix stops matching — and you get an answer with zero cache hits and &lt;em&gt;no error&lt;/em&gt; telling you. You just silently pay full price.&lt;/p&gt;

&lt;p&gt;So test it on your gateway specifically: send the same request twice and confirm &lt;code&gt;cache_read_input_tokens&lt;/code&gt; is non-zero on the second. On the one I use, &lt;code&gt;cache_control&lt;/code&gt; and the usage fields pass through to the official upstream unchanged, so the cache key I build is the one that actually gets used and the 0.1x reads show up — but "does my proxy preserve caching" is a question you should answer with the usage object, not take on faith from anyone, me included.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole lesson in three lines
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Caching has a &lt;strong&gt;write premium&lt;/strong&gt; (~1.25x); reads are cheap (0.1x). It's amortization — you need repeat reads.&lt;/li&gt;
&lt;li&gt;The prefix must be &lt;strong&gt;byte-stable and up front&lt;/strong&gt;. A timestamp in your system prompt will quietly cost you money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch &lt;code&gt;cache_read_input_tokens&lt;/code&gt;.&lt;/strong&gt; Zero on a repeat call means it's not working, whatever the marketing said.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want the reference version — the breakpoint rules, the OpenAI side, the exact usage fields per provider — I wrote it up &lt;a href="https://docs.byesu.com/en/guide/prompt-caching" rel="noopener noreferrer"&gt;over here&lt;/a&gt;. But the timestamp story above is really the whole lesson.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>caching</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>GPT-5.6 Sol vs Terra vs Luna: which tier should you actually use?</title>
      <dc:creator>amdmsz</dc:creator>
      <pubDate>Sat, 11 Jul 2026 22:01:53 +0000</pubDate>
      <link>https://dev.to/amdmsz/gpt-56-sol-vs-terra-vs-luna-which-tier-should-you-actually-use-2elm</link>
      <guid>https://dev.to/amdmsz/gpt-56-sol-vs-terra-vs-luna-which-tier-should-you-actually-use-2elm</guid>
      <description>&lt;p&gt;When GPT-5.6 landed as &lt;em&gt;three&lt;/em&gt; models instead of one, my first reaction was mild annoyance. Sol, Terra, Luna — great names, zero help when I'm staring at a config file deciding which string to paste into &lt;code&gt;model&lt;/code&gt;. So I did the boring thing: I wired all three into the same app, ran a week of real traffic through them, watched the token meter, and wrote down what I learned. This is that write-up — the decision tree I wish someone had handed me on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-second version
&lt;/h2&gt;

&lt;p&gt;Three tiers, same API shape, same features. The only thing that changes is depth vs. cost vs. latency. Official OpenAI list prices, per million tokens:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Model string&lt;/th&gt;
&lt;th&gt;List price (in / out)&lt;/th&gt;
&lt;th&gt;My one-liner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sol&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-sol&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$5 / $30&lt;/td&gt;
&lt;td&gt;The flagship. Reach for it when a wrong answer is expensive.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terra&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-terra&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$2.50 / $15&lt;/td&gt;
&lt;td&gt;The default that surprised me.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Luna&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-luna&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$1 / $6&lt;/td&gt;
&lt;td&gt;The volume workhorse.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note the shape of that output column: $30, $15, $6. Output tokens are where the money goes, and they scale 5:1 against input across all three. Keep that ratio in your head — it makes tier choice mostly a question of &lt;em&gt;how much the model talks&lt;/em&gt;, not how much you feed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terra is the plot twist
&lt;/h2&gt;

&lt;p&gt;I expected to run Sol everywhere and grumble about the bill. Then I A/B'd Sol against Terra on my actual coding-assistant traffic — diffs, refactors, "why is this test flaky" spelunking. OpenAI's own line is that Terra hits about &lt;strong&gt;97% of Sol's benchmark performance&lt;/strong&gt;, and honestly? On day-to-day dev work I couldn't feel the missing 3%. Same fixes, same explanations, half the list price.&lt;/p&gt;

&lt;p&gt;That reframed the whole exercise for me. The question stopped being "can I afford Sol?" and became "do I have a &lt;em&gt;specific&lt;/em&gt; reason to escalate off Terra?" For most requests the answer is no. Terra became my baseline and Sol became the exception I reach for deliberately — not the reverse.&lt;/p&gt;

&lt;p&gt;Where Sol still earns its keep for me: genuinely hard reasoning where the cost of being wrong dwarfs the token bill. Architecture reviews, gnarly migrations, research synthesis across a big pile of context. And &lt;strong&gt;ultra mode&lt;/strong&gt; — the 5.6 family can orchestrate parallel sub-agents on complex tasks, and that coordination is exactly the kind of work where the deepest tier pays for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Luna is not a downgrade, it's a different job
&lt;/h2&gt;

&lt;p&gt;Luna is the one people mis-read. It's not "watered-down Sol," it's the tier you point at work where per-token cost dominates and the quality ceiling basically never binds: bulk classification, tagging, extraction, summarizing a firehose of records. When you're doing the same small operation ten thousand times, a dollar of input vs. five dollars of input is the entire P&amp;amp;L. Luna is also the fastest of the three, so it's my pick for anything latency-sensitive — autocomplete, a streaming chat UI — paired with &lt;code&gt;stream: true&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cache math nobody puts on the slide
&lt;/h2&gt;

&lt;p&gt;Here's the part that actually changed my routing, and it's the reason I'd tell you &lt;em&gt;not&lt;/em&gt; to just default to the smallest tier.&lt;/p&gt;

&lt;p&gt;GPT-5.6 ships with &lt;strong&gt;predictable caching&lt;/strong&gt;: a prompt prefix is guaranteed to stay cached for at least 30 minutes, and you can drop your own cache breakpoints. Cache &lt;em&gt;reads&lt;/em&gt; bill at &lt;strong&gt;10% of the input price&lt;/strong&gt;. That number quietly rewrites the arithmetic for any prefix-heavy workload.&lt;/p&gt;

&lt;p&gt;Think about a RAG setup where every request re-sends the same fat corpus prefix. Without caching you pay full input rate on that prefix every single call. Pin it behind a breakpoint and you pay full rate &lt;em&gt;once&lt;/em&gt; per 30-minute window, then 10% on every hit after. Run the numbers on a realistic prefix-to-suffix ratio and Terra-with-cache can land &lt;em&gt;below&lt;/em&gt; Luna-uncached on effective per-request cost — while giving you Terra-grade answers. I stopped reaching for the smallest tier reflexively and started modeling the prefix ratio first. Sometimes the "more expensive" tier is the lower-cost system.&lt;/p&gt;

&lt;h2&gt;
  
  
  My actual routing rules
&lt;/h2&gt;

&lt;p&gt;After all that, here's the decision tree I run in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deep reasoning / high-stakes&lt;/strong&gt; (architecture, tricky migrations, ultra-mode agent pipelines) → &lt;strong&gt;Sol&lt;/strong&gt;. Output quality wins when a mistake is expensive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everyday work&lt;/strong&gt; (coding assistant, general chat, most product features) → &lt;strong&gt;Terra&lt;/strong&gt;. ~97% of Sol at half the list price; escalate specific request &lt;em&gt;types&lt;/em&gt; to Sol only when your evals show the gap is real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-frequency / bulk&lt;/strong&gt; (classification, extraction, summarization, latency-critical UX) → &lt;strong&gt;Luna&lt;/strong&gt;, with &lt;code&gt;stream&lt;/code&gt; on where it helps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The meta-rule: start on Terra, promote to Sol by request-type when evals justify it, route the bulk lane to Luna. Match the workload, not the badge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying all three behind one key (no OpenAI account)
&lt;/h2&gt;

&lt;p&gt;The nice part is that testing this costs almost nothing in effort. I ran all three tiers through &lt;strong&gt;byesu&lt;/strong&gt; — an AI API gateway that speaks the OpenAI-compatible Chat Completions API (and an Anthropic-native &lt;code&gt;/v1/messages&lt;/code&gt; endpoint from the same host, same token). One &lt;code&gt;sk-&lt;/code&gt; key covers all three GPT-5.6 tiers, so comparing them is a one-string change in a loop, and billing is pay-as-you-go per token — no subscription, no separate OpenAI account to provision.&lt;/p&gt;

&lt;p&gt;If you already have OpenAI SDK code, it's a &lt;code&gt;base_url&lt;/code&gt; swap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk-YOUR_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://byesu.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Refactor this function and explain the trade-offs.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tier&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.6-sol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tier&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt_tokens&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  out=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completion_tokens&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log &lt;code&gt;usage&lt;/code&gt; on every call — that &lt;code&gt;in&lt;/code&gt;/&lt;code&gt;out&lt;/code&gt; split is the whole game. Once you can see input vs. output tokens per tier on &lt;em&gt;your&lt;/em&gt; prompts, the pricing table above stops being abstract and the right tier basically picks itself.&lt;/p&gt;

&lt;p&gt;One gotcha worth flagging: when you create the token, put it in the &lt;strong&gt;OpenAI GPT group&lt;/strong&gt;. Wrong group is the usual cause of a "no available channel" error, and the model string has to be exactly &lt;code&gt;gpt-5.6-sol&lt;/code&gt; / &lt;code&gt;-terra&lt;/code&gt; / &lt;code&gt;-luna&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Default to &lt;strong&gt;Terra&lt;/strong&gt;. Escalate to &lt;strong&gt;Sol&lt;/strong&gt; for the handful of requests where being right is worth $30-per-million output. Push bulk and latency-sensitive lanes to &lt;strong&gt;Luna&lt;/strong&gt;. And before you assume the smallest tier is the lowest-cost one, do the cache math — predictable caching plus 10% reads can flip the ranking entirely. Wire all three behind one key, log your token usage, and let your own traffic settle the argument.&lt;/p&gt;

</description>
      <category>openai</category>
      <category>ai</category>
      <category>gpt</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I ran Claude Code on Grok 4.5 — here's the 3-line setup</title>
      <dc:creator>amdmsz</dc:creator>
      <pubDate>Sat, 11 Jul 2026 21:27:11 +0000</pubDate>
      <link>https://dev.to/amdmsz/i-ran-claude-code-on-grok-45-heres-the-3-line-setup-560i</link>
      <guid>https://dev.to/amdmsz/i-ran-claude-code-on-grok-45-heres-the-3-line-setup-560i</guid>
      <description>&lt;p&gt;I've basically lived inside Claude Code for the last few months. Opus 4.8 is my daily driver and I'm not looking to leave it. But when xAI dropped &lt;strong&gt;Grok 4.5&lt;/strong&gt; on July 8, two things nagged at me enough that I wanted it in the exact same terminal I already had open.&lt;/p&gt;

&lt;p&gt;This is the write-up of that afternoon — what pulled me in, the three lines that actually did the job, and where I landed on Grok vs Opus after living with both for a week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I even bothered
&lt;/h2&gt;

&lt;p&gt;Two reasons, and neither of them is "it's the smartest model in the world," because it isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One: it's built for coding.&lt;/strong&gt; Grok 4.5 is the first xAI model explicitly aimed at coding and agentic work — xAI says it was trained on real developer sessions from Cursor, so the pitch is "long stretches of work inside an actual repo," which is exactly what Claude Code does all day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two: the token efficiency number stuck with me.&lt;/strong&gt; In the third-party write-ups going around, Grok 4.5 finished the same agentic indexing task in roughly &lt;strong&gt;14k output tokens&lt;/strong&gt; where a frontier flagship burned closer to &lt;strong&gt;67k&lt;/strong&gt;. When you run an agent that loops and re-reads files for twenty minutes, output tokens &lt;em&gt;are&lt;/em&gt; the bill. A model that says the same thing in a quarter of the tokens is interesting even before you look at the per-token rate.&lt;/p&gt;

&lt;p&gt;Official rate, for the record: xAI lists Grok 4.5 at &lt;strong&gt;$2 / $6 per million&lt;/strong&gt; input/output, and claims it comes in 60%+ under the current flagship tier. Fewer tokens &lt;em&gt;and&lt;/em&gt; a lower rate stacks. That was enough to make me curious.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing I kept forgetting
&lt;/h2&gt;

&lt;p&gt;Here's the part that took me embarrassingly long to internalize: &lt;strong&gt;Claude Code doesn't actually care what model is on the other end.&lt;/strong&gt; It's a CLI that speaks Anthropic's &lt;code&gt;/v1/messages&lt;/code&gt; protocol to whatever &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; points at. If something answers in that shape, Claude Code will happily drive it.&lt;/p&gt;

&lt;p&gt;So I didn't need a Grok-specific client. I needed an endpoint that (a) speaks Anthropic-native and (b) has grok-4.5 sitting behind it. I already had an account on &lt;strong&gt;byesu&lt;/strong&gt;, an API gateway that exposes both an OpenAI-compatible and an &lt;strong&gt;Anthropic-native&lt;/strong&gt; endpoint over a pile of models — pay-as-you-go, and Grok 4.5 was already listed. That's the whole trick: the gateway translates, Claude Code never knows the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three lines
&lt;/h2&gt;

&lt;p&gt;That's it. Literally three environment variables:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://byesu.com
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-your-own-key
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;grok-4.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in any project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ANTHROPIC_MODEL=grok-4.5&lt;/code&gt; is the line doing the interesting work — it pins every request to Grok instead of the default Claude model, so you don't have to remember to switch each session. If you'd rather flip between models mid-session, drop that third line and use &lt;code&gt;/model&lt;/code&gt; inside the REPL instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two gotchas so you skip my afternoon
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No &lt;code&gt;/v1&lt;/code&gt; on the base URL.&lt;/strong&gt; The Anthropic-native endpoint is the bare root — &lt;code&gt;https://byesu.com&lt;/code&gt;, not &lt;code&gt;https://byesu.com/v1&lt;/code&gt;. I pasted in a &lt;code&gt;/v1&lt;/code&gt; out of muscle memory from OpenAI-style setups and got a wall of connection errors. Strip it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If auth won't take, swap the key variable.&lt;/strong&gt; On one machine my first call kept bouncing on auth. The fix was replacing &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; with &lt;code&gt;ANTHROPIC_AUTH_TOKEN&lt;/code&gt; (same value) — some Claude Code versions prefer one over the other. Thirty seconds once you know; a genuinely confusing ten minutes if you don't.&lt;/p&gt;

&lt;p&gt;Sanity check before you trust it: run &lt;code&gt;claude&lt;/code&gt;, ask it something trivial, and confirm the model line in the status bar reads grok-4.5. If it still says a Claude model, your &lt;code&gt;ANTHROPIC_MODEL&lt;/code&gt; export didn't make it into that shell — re-source it or reopen the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line vibe check
&lt;/h2&gt;

&lt;p&gt;It's fast, and it's &lt;em&gt;terse&lt;/em&gt; in a way I ended up liking. Grok 4.5 doesn't pad. It reads the files, makes the edit, and moves on with noticeably less "let me explain my reasoning at length" preamble — which, combined with the token-efficiency thing, means the agent loop just feels tighter. For mechanical work — wiring up a route, refactoring a module, chasing a stack trace across a few files — it kept pace with what I'd normally reach for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I actually landed: Grok vs Opus 4.8
&lt;/h2&gt;

&lt;p&gt;I'm not going to tell you Grok 4.5 replaces Opus 4.8, because after a week it clearly doesn't — for me they split by task, not by "better/worse."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Opus 4.8&lt;/strong&gt; is still where I go for the gnarly stuff: ambiguous architecture calls, "read this whole subsystem and tell me what's actually wrong," the reasoning-heavy work where I want the model to sit and think. It's the frontier flagship and it feels like it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grok 4.5&lt;/strong&gt; is where I go for volume. The high-frequency, well-specified, mechanical passes where I know roughly what needs to happen and I just want it done in fewer tokens and fewer words. On independent coding benchmarks it lands in the same neighborhood as the top-tier models at a fraction of the per-task cost, and Artificial Analysis had it at #4 on their intelligence index at launch — which is a lot of "good enough" for the kind of work that eats most of my day.&lt;/p&gt;

&lt;p&gt;So it's complementary, not a swap. The nice part of the gateway setup is I don't have to commit: same three env vars, change the model line (or &lt;code&gt;/model&lt;/code&gt;), and I'm on whichever one fits the task. Opus for the thinking, Grok for the grinding.&lt;/p&gt;

&lt;p&gt;If you want the reference version of the setup — supported models, endpoints, the OpenAI-compatible side — it's documented over at &lt;a href="https://docs.byesu.com/en/models/grok-4-5" rel="noopener noreferrer"&gt;docs.byesu.com&lt;/a&gt;. But honestly, the three lines above are the whole story. Export, &lt;code&gt;claude&lt;/code&gt;, go.&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>coding</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
