"""Helpers for the Gemini core-generation smoke tests: free-tier pacing (15 RPM/model) and one 429 retry.""" from __future__ import annotations import base64 import re import struct import time import zlib _LAST_CALL = [0.0] MIN_GAP_S = 4.2 def call(gemini, method: str, path: str, body=None, retries: int = 2, **kw): """gemini fixture wrapper: keeps >= MIN_GAP_S between calls; retries on 429 (RetryInfo delay) and transient 503.""" for attempt in range(retries + 1): gap = MIN_GAP_S - (time.time() - _LAST_CALL[0]) if gap > 0: time.sleep(gap) st, resp, hdrs = gemini(method, path, body, **kw) _LAST_CALL[0] = time.time() if st not in (429, 503) or attempt == retries: return st, resp, hdrs msg = resp.get("error", {}).get("message", "") if isinstance(resp, dict) else "" m = re.search(r"retry in ([0-9.]+)s", msg) time.sleep(float(m.group(1)) + 1 if m else 20) return st, resp, hdrs def png_1x1_b64() -> str: def ch(t, d): c = struct.pack(">I", len(d)) + t + d return c + struct.pack(">I", zlib.crc32(t + d) & 0xFFFFFFFF) png = b"\x89PNG\r\n\x1a\n" + ch(b"IHDR", struct.pack(">IIBBBBB", 1, 1, 8, 2, 0, 0, 0)) + ch(b"IDAT", zlib.compress(b"\x00\xff\x00\x00")) + ch(b"IEND", b"") return base64.b64encode(png).decode() def user(text: str) -> list[dict]: return [{"role": "user", "parts": [{"text": text}]}] def text_of(body: dict) -> str: return "".join(p.get("text", "") for p in body["candidates"][0]["content"]["parts"] if not p.get("thought")) def gc(model: str) -> str: return f"/v1beta/models/{model}:generateContent"