◉GEMINI LABJP
●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — The old Deep Research agent shut down on 10/23. If calls fail, move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…●ARGON — gemini-4-argon is generally available as of 10/13, aimed at complex coding, agents, math and science●DEEP RES — The old Deep Research agent shut down on 10/23. If calls fail, move to deep-research-preview-04-2026●NANO — gemini-nano-banana-2.1 is GA (10/06). gemini-3.1-flash-image is deprecated with no shutdown date yet●MCP — One write-up cut an 85,000-token tool result to 310 tokens by wrapping it in an ADK FunctionTool●FLASH — gemini-3.7-flash and 3.5-flash now route automatically to newer Flash models (10/08)●NEW — Stop Re-Reading Every Crash Report: Gre…
Articles/Advanced
⬡ Advanced/2026-07-28Advanced

When Version Numbers Stopped Meaning Generations: Rebuilding Cost Attribution That Parsed Model IDs

A regex that derived generation and tier from Gemini model IDs broke quietly once Flash reached 3.6 while the top Pro stayed at 3.5. Here are the runnable probes, the attribution gap between regex-derived and registry-joined rollups, and the redesign that treats model IDs as opaque keys.

Gemini API251model managementcost attributionobservability14design

✦ Premium Article

During the week of July 21, a band on my dashboard labeled "3.5 series" started sliding in a way I could not explain.

Call volume had not moved. Only the share of output tokens was falling. Nothing smelled like an outage, yet the numbers were quietly drifting.

Tracing it back, the cause was neither a model shutdown nor a prompt change. My rollup layer was reading generation and tier out of the model ID string, and that reading had lost its meaning the moment the lineup stopped lining up.

I run a wallpaper app's classification pipeline and a handful of automations as an indie developer. Every time a new call site appeared, I treated the model ID as a string packed with information rather than as a key. It was convenient — and it failed just as quietly.

The moment "3.5 series" stopped describing anything

The implementation was ordinary. A daily usage table keyed by model_id, and a dashboard layer applying a regex to derive generation and tier. Adding a model required no dashboard change, so it served me well for months.

It rested on two assumptions:

  1. The number in a model ID marks the generation, and larger means newer
  2. The third hyphen-delimited token marks the tier (flash or pro)

The July 2026 lineup satisfies neither. The newest Flash is 3.6, the top Pro is 3.5, the image model sits at 3.1, and Omni Flash carries no number at all. The digits no longer track generation, and they never tracked capability.

Model IDPositionReadable from the number?
gemini-3.6-flashNewer Flash release (July 21)Yes
gemini-3.5-proTop of the Pro lineNumber is lower than 3.6
gemini-3.5-flash-liteLightweight, high-volume workThird token reads flash
gemini-3.1-flash-lite-imageImage generation and editingThird token reads flash
gemini-omni-flashVideo generation previewNo number
imagen-4.0-generate-001Image model scheduled for shutdownDifferent prefix

Model names and availability move. If you keep a local table, confirm it against the official model list and the pricing page before acting on it.

Running the regex against the actual lineup

Rather than argue about it, I pulled the regex I had been using and ran it over the real IDs.

# parse_probe.py — the old derivation, applied to real IDs
import re
 
MODELS = [
    "gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite",
    "gemini-3.5-flash-cyber", "gemini-3.5-pro", "gemini-3.1-flash-lite-image",
    "gemini-omni-flash", "gemini-flash-latest", "imagen-4.0-generate-001",
]
 
PAT = re.compile(r"^gemini-(?P<major>\d+)\.(?P<minor>\d+)-(?P<tier>[a-z]+)")
 
print(f"{'model_id':<30} {'gen':<8} {'tier':<8} note")
print("-" * 62)
for m in MODELS:
    hit = PAT.match(m)
    if not hit:
        print(f"{m:<30} {'-':<8} {'-':<8} UNPARSED -> falls to default bucket")
        continue
    gen = f"{hit['major']}.{hit['minor']}"
    print(f"{m:<30} {gen:<8} {hit['tier']:<8} parsed")

The output:

model_id                       gen      tier     note
--------------------------------------------------------------
gemini-3.6-flash               3.6      flash    parsed
gemini-3.5-flash               3.5      flash    parsed
gemini-3.5-flash-lite          3.5      flash    parsed
gemini-3.5-flash-cyber         3.5      flash    parsed
gemini-3.5-pro                 3.5      pro      parsed
gemini-3.1-flash-lite-image    3.1      flash    parsed
gemini-omni-flash              -        -        UNPARSED -> falls to default bucket
gemini-flash-latest            -        -        UNPARSED -> falls to default bucket
imagen-4.0-generate-001        -        -        UNPARSED -> falls to default bucket

The shape of the failure is what mattered.

The three unparsed IDs are recoverable. They land in a - bucket, so a glance at the chart tells you something unfamiliar has arrived.

The four that parsed are the dangerous ones. flash-lite, flash-cyber, and flash-lite-image all pass through as tier=flash. No exception is raised. Nothing lands in a log. The values simply blend.

Parse errors are visible. Semantic mismatches are not. The half day I lost went to the second kind.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A probe run against the real July 2026 lineup showing flash-lite and flash-lite-image silently collapsing into the flash bucket
✦Three reasonable implementations of pick the newest model returning gemini-3.5-pro, gemini-3.6-flash, and gemini-3.10-flash
✦A SQLite harness where output tokens per call reads 26.52 under regex rollup and 100.00 under a registry join — a 3.8x gap
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ Advanced2026-09-24
I Stopped Asking Gemini 'Is This Right?' and Started Hiding My Answer Instead
Ask Gemini to confirm a label you already chose and it tends to agree. Here is why I compared three ways of asking, chose blind re-classification, how my answer still leaked in, and the routing code that sends only disagreements to a human.
⬡ Advanced2026-09-14
When BLOCK_NONE Changes Nothing: Telling Adjustable Gemini Blocks from the Ones You Can't Move
Some safety blocks go away when you loosen a threshold. Others never will. Here is how to read the response and decide which kind you are looking at before you touch a single setting.
⬡ Advanced2026-09-05
Two Kinds of Video Questions: Why I Send "Find It" and "Prove It Isn't There" Down Separate Paths
Agentic video understanding lets the model decide which parts of a video to watch. That works beautifully for finding things, and it quietly breaks when you need to prove something never appears. Here is how I split my questions, and the coverage check I now run first.