GEMINI LABJP
API — The Gemini API release notes still end at Lyria 3.5 on September 3. Nothing new had appeared as of September 10CLI — Gemini CLI v0.47.0 nightly adds documentation and migration commands for the Antigravity CLI. Worth remembering that this is a nightly buildGARDEN — Claude Fable 5.1 has joined Model Garden on the Gemini Enterprise Agent Platform, alongside updated embedding SKUs and agent metering pricesDEPRECATION — gemini-omni-flash-preview retires on September 30, twenty days out. The path forward is gemini-omni-1.1-flash, which reached GA on August 27SIRI — iOS 27 ships on September 14. Its rebuilt Siri was reportedly developed with help from Google's Gemini models, though the specifics remain unconfirmedPRICE — The $0.75 / $3.75 per MTok on Gemini 3.8 Flash and 3.7 Flash is introductory. From January 1, 2027 it becomes $1.50 / $7.50API — The Gemini API release notes still end at Lyria 3.5 on September 3. Nothing new had appeared as of September 10CLI — Gemini CLI v0.47.0 nightly adds documentation and migration commands for the Antigravity CLI. Worth remembering that this is a nightly buildGARDEN — Claude Fable 5.1 has joined Model Garden on the Gemini Enterprise Agent Platform, alongside updated embedding SKUs and agent metering pricesDEPRECATION — gemini-omni-flash-preview retires on September 30, twenty days out. The path forward is gemini-omni-1.1-flash, which reached GA on August 27SIRI — iOS 27 ships on September 14. Its rebuilt Siri was reportedly developed with help from Google's Gemini models, though the specifics remain unconfirmedPRICE — The $0.75 / $3.75 per MTok on Gemini 3.8 Flash and 3.7 Flash is introductory. From January 1, 2027 it becomes $1.50 / $7.50
Articles/Dev Tools
Dev Tools/2026-09-10Intermediate

Matching name variants in a print catalog: normalize first, then ask Gemini for candidates

When the same artist appears under three spellings, asking Gemini whether two names are the same person quietly merges different generations. Here is the two-layer alternative — deterministic normalization first, model-generated candidates second — with working code and a cost estimate.

Gemini API237Structured Output10Data CleanupIndie Development16Ukiyo-e

Premium Article

I was reading through the catalog one evening, before a batch of ukiyo-e wallpapers went out, when I noticed the same artist sitting in three different rows. "歌川広重", "安藤広重", "Utagawa Hiroshige". None of them is wrong. To my script, they were three different people.

I scan old woodblock prints and repair the creases and stains by hand, one sheet at a time. That part of the work has not changed tools. The metadata I attach afterwards — artist name, title, series — is typed by a person, so it drifts.

My first attempt was to have Gemini absorb that drift. It took me a while to understand why that was the wrong shape.

What happened the day I asked "are these the same person?"

The first function I wrote handed two names to the model and asked whether they referred to the same artist. Most of the time the answer was right. The problem lived in the handful of cases where it was not.

Hiroshige and the pupil who later took the name as the second Hiroshige came back as one person. The answer arrived as a flat "yes, the same artist," so in a spreadsheet it looked exactly like every correct answer. I shipped a few prints with the wrong attribution before I caught it.

The model supplies material; I make the call. Getting to that line helped, but stopping the yes/no question was not enough on its own. The real waste was the number of calls. I was asking a language model to re-derive the same spelling drift, row after row.

Layer one: normalization belongs in code

Most of the drift is not a question of meaning. Full-width and half-width characters, old and new kanji forms, Hepburn long vowels, parenthetical notes — all of it is mechanical. There is no reason to spend inference on it.

# normalize.py — collapse everything a rule can collapse, before any API call
import re
import unicodedata
 
# old/variant kanji forms -> modern forms (I add entries as the catalog surfaces them)
KYUJI = str.maketrans({
    "廣": "広", "國": "国", "齋": "斎", "澤": "沢",
    "溪": "渓", "藝": "芸", "驛": "駅", "豐": "豊",
})
 
NUM_FIX = [("拾", "十"), ("卅", "三十")]
 
# parentheses, trailing role markers, circled numerals
DECOR = re.compile(r"[((\[][^))\]]*[))\]]|$|$|[①-⑳]")
 
 
def normalize(name: str) -> str:
    s = unicodedata.normalize("NFKC", name)      # unify full-width and half-width forms
    s = s.translate(KYUJI)
    for a, b in NUM_FIX:
        s = s.replace(a, b)
    s = DECOR.sub("", s)
    s = re.sub(r"[\s・,.'’\-]", "", s)            # drop separators entirely
    return s.lower()
 
 
def romaji_key(name: str) -> str:
    """Collapse long-vowel spellings: Hiroshige / Hirōshige / Hiroshige."""
    s = unicodedata.normalize("NFKD", name)
    s = "".join(c for c in s if not unicodedata.combining(c))  # ō -> o
    s = re.sub(r"(ou|oo|uu)", lambda m: m.group(0)[0], s.lower())
    return re.sub(r"[^a-z]", "", s)
 
 
if __name__ == "__main__":
    for a, b in [("歌川廣重", "歌川広重"), ("Utagawa Hirōshige", "Utagawa Hiroshige")]:
        key = normalize if re.search(r"[ぁ-んァ-ヶ一-龥]", a) else romaji_key
        print(a, b, key(a) == key(b))
        # expected output:
        # 歌川廣重 歌川広重 True
        # Utagawa Hirōshige Utagawa Hiroshige True

In my catalog, this function alone dropped most of the variants onto a single key. What survived were pairs like "歌川広重" and "安藤広重", which no amount of character folding will ever connect. That is where the model earns its place.

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
You will be able to draw the line yourself between what normalization should handle and what the model should handle, before a name-matching job breaks in a way nobody notices
You will have a concrete way to stabilize output using response_schema and system_instruction, instead of relying on a temperature setting that is no longer honored
You will know how to run a thousand-row reconciliation for around one dollar, mostly by cutting the number of calls you make at all
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 $15 for lifetime access
View Membership →

Related Articles

Advanced2026-07-15
A near-miss label won't fix itself on retry — a normalization layer for closed-vocabulary classification
When responseSchema enum returns an out-of-set label, retrying tends to return the same near-miss. From a wallpaper app's 30-category batch, here is the distribution of how labels miss, plus a normalization layer built on an alias table and gemini-embedding-2 nearest-neighbor, with measured results.
API / SDK2026-06-12
Building an App Store Rejection Workflow with the Gemini API — From Structured Notices to Resolution Center Replies
How I use the Gemini API to parse App Store rejection notices into structured JSON, cross-check guidelines, draft Resolution Center replies, and run pre-submission checks as an indie developer.
Dev Tools2026-09-06
Icon-Only Buttons Can Stay Silent for Screen Readers, Even When Every Translation Is in Place
A fully translated app can still read as silence. Here is how I collect icon elements and labels from iOS and Android, settle everything mechanical locally, and send only the wording to Gemini — with the actual output from each stage.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links