Log requested vs stored model ids to find retired ids still in config

Sume runs a retired image id as its successor and stores the successor on the job. Compare the two ids after each submit to find stale config.

5 min readSume
All posts

When a model is retired on Sume, a request that names it still succeeds, and the job stores the successor id. That makes the stored model on the job the cheapest drift detector you have: compare it with the id you sent, and log a warning when they differ. A retired id in your config then shows up in a log line the first day, instead of in a cost report next month.

Where the two ids are visible

The Image API docs say google/nano-banana-2 and nano-banana-2 run as Nano Banana 2.1 and that the job stores the 2.1 id. For sume/auto the stored model stays sume/auto, so a difference there is expected and should not warn. Bare legacy ids such as gpt-image-2 are accepted as aliases for their org-prefixed form, so a prefix-only difference is also expected.

Requested versus stored model, as of 2026-10-08
RequestedStored on the jobWarn?
google/nano-banana-2google/nano-banana-2.1Yes: retired id in config
nano-banana-2google/nano-banana-2.1Yes: retired id in config
gpt-image-2openai/gpt-image-2 (org-prefixed form)No: alias only
sume/autosume/autoNo

Make the comparison strict enough

Strip the organization prefix before comparing, so the alias case stays quiet, and skip sume/auto. Anything else that differs is a real substitution. Keep the check in the code path that already reads the job, so it costs no extra request. If you submit with mode: "async", read the job once on completion and run the check there.

A warning is not an outage. The request worked, and the point is to update config on your schedule. Pin the successor id, redeploy, and the warning disappears.

The check

The helper takes the id you sent and the job envelope, and returns a message or None.

def bare(model_id: str) -> str:
    return model_id.split("/")[-1]

def drift(requested: str, job: dict):
    stored = job.get("model") or (job.get("job") or {}).get("model")
    if not stored or requested == "sume/auto":
        return None
    if bare(requested) == bare(stored):
        return None
    return f"config asks {requested}, job ran {stored}: update the id"

print(drift("nano-banana-2", {"model": "google/nano-banana-2.1"}))
print(drift("gpt-image-2", {"model": "openai/gpt-image-2"}))
print(drift("sume/auto", {"model": "sume/auto"}))

Pair it with a startup check

Drift detection after the fact works well with a startup check against GET /v1/images/models, which no longer lists retired ids. The startup check stops a deploy. The log check catches the old id that slipped through.

What to do with the warnings

Collect the warning lines for a week and sort them by requested id. Each distinct pair is one config line to change. Ship the change to the successor id, and the line stops appearing. Keep one test that feeds a retired id and expects a warning, so the check cannot silently rot.

Do not make the warning fatal in production. The docs describe retired ids as still working, and an outage caused by a log rule is worse than a stale id. Make it fatal in CI, where a pinned id that no longer matches the catalog should fail a build.

The same idea applies beyond images, but check the specific docs for each family before you assume a successor mapping. The image docs state the Nano Banana 2 rule directly. For video, the catalog is read from GET /v1/videos/models, and an unknown id is a model not found error, not a silent alias, so a startup check is the right tool there.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume