8月の停止予定を並べた changelog を眺めたあと、手元のテストスイートを走らせてみました。6件すべて緑、0.11秒。その緑の下に、8月17日に停止するモデルの録画レスポンスが3本眠っていました。
個人開発のアプリで画像生成パイプラインを検討している私の環境では、Gemini API のテストをすべて録画済みレスポンス(cassette)の再生で回しております。課金を伴うAPIをCIのたびに叩きたくないので、これは真っ当な設計のはずでした。ただ、モデルの停止という失敗クラスに限っては、この設計そのものが検知を妨げる側に回ります。
テストが通ることと、来週も本番が動くことは、別の命題です。この2つを繋ぎ直す小さな仕組みを作り、手元で数字を取りました。
8月に止まるものと、モックが原理的に検知しないもの
まず前提の整理です。2026年8月は停止・撤去が集中しています。
| 対象 | 期日 | 影響 |
|---|---|---|
Imagen 4 系(imagen-4.0-generate-001 / ultra / fast)・Gemini 3 Image 系 | 2026-08-17 停止 | 画像生成パイプラインは移行必須 |
| Gemini Enterprise Agent Platform 上の Grok 4.1 ファミリー | 2026-08-20 停止 | 該当エージェント構成の差し替え |
gemini-robotics-er-1.6-preview | 2026-08-31 停止 | プレビュー依存の経路は代替が必要 |
| Gemini Enterprise(global リージョン)の 3.5 Flash | 2026-08-04 撤去済み | モデル一覧から選択肢が消える |
期日と対象は変わり得るため、実作業の前にモデルの非推奨スケジュールで一次情報を確認してください。
ここで問題になるのが、テストの作りです。録画レスポンスの再生・SDKのモック・スタブサーバー — 呼び名は何であれ、これらは「ネットワークの向こう側を固定する」ための仕組みです。レイテンシの揺れも、レートリミットも、そしてモデルの停止も、固定した瞬間にテストの観測範囲から消えます。
つまりモデル停止は、モックが隠すために設計された失敗クラスに、ちょうど嵌まる形をしています。カバレッジを上げるほど検知が遅れるという、嫌な逆相関です。
緑のまま止まる構造を、最小構成で再現する
言葉で説明するより、動くもので示した方が早いはずです。壁紙アプリの画像生成を想定した最小クライアントを書きました。
# client.py
import os
import requests
BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
class GeminiImageClient:
def __init__(self, model: str, api_key: str | None = None, base_url: str = BASE_URL):
self.model = model
self.api_key = api_key or os.environ.get("GEMINI_API_KEY", "")
self.base_url = base_url # テスト時にスタブへ向け替えるための注入口
def generate(self, prompt: str, aspect_ratio: str = "9:16") -> dict:
url = f"{self.base_url}/models/{self.model}:predict"
resp = requests.post(
url,
params={"key": self.api_key},
json={"instances": [{"prompt": prompt}],
"parameters": {"sampleCount": 1, "aspectRatio": aspect_ratio}},
timeout=30,
)
resp.raise_for_status()
return resp.json()テスト側は、録画したレスポンスをJSONで持ち、requests.post を差し替えて再生します。
# tests/conftest.py(再生部分)
import json
import pathlib
import pytest
CASSETTE_DIR = pathlib.Path(__file__).parent / "cassettes"
class _FakeResponse:
def __init__(self, payload, status=200):
self._payload = payload
self.status_code = status
def raise_for_status(self):
if self.status_code >= 400:
import requests
raise requests.HTTPError(f"{self.status_code}")
def json(self):
return self._payload
@pytest.fixture
def replay(monkeypatch):
def _use(cassette_name):
data = json.loads((CASSETTE_DIR / cassette_name).read_text())
def fake_post(url, **kwargs):
return _FakeResponse(data["response"], data.get("status", 200))
monkeypatch.setattr("requests.post", fake_post)
return data
return _usecassette には録画日時とリクエスト情報を残しておきます。この設計判断があとで効いてきます。
{
"recorded_at": "2026-06-20T09:12:44+09:00",
"request": {
"model": "imagen-4.0-fast-generate-001",
"endpoint": ":predict",
"parameters": {"sampleCount": 1, "aspectRatio": "9:16"}
},
"response": {
"predictions": [{"bytesBase64Encoded": "iVBORw...", "mimeType": "image/png"}],
"modelVersion": "imagen-4.0-fast-generate-001"
}
}この構成で、生成結果の検証・アスペクト比・バッチ生成・セーフティフィルタ時の空応答など6件のテストを書きました。VM(Python 3.10.12)での結果は次の通りです。
$ python3 -m pytest tests/ -q
...... [100%]
6 passed in 0.11s
全部緑です。一方で、録画元の imagen-4.0-fast-generate-001 は8月17日に停止します。停止後の実APIの挙動は手元ではまだ確認できないため、既に停止済みの旧モデル群が返す NOT_FOUND の形状を再現したローカルスタブを立てて対比しました。
imagen-4.0-fast-generate-001: HTTPError 404 Client Error: Not Found (4.1ms)
gemini-3.1-flash-lite-image: OK modelVersion=gemini-3.1-flash-lite-image (2.2ms)
テストは0.11秒で全部通り、同じコードの実呼び出しは404で落ちる。この乖離が「緑のまま止まる」の正体です。停止済みモデルへ実際にリクエストを投げ続けるとどうなるかは、以前停止済みモデルの再試行は、成功レイテンシには出ませんでしたで実測しております。恒久エラーは平常時のメトリクスに映りにくく、そちらでも観測の穴になりがちでした。