Elf universelle Hintergründe von grafisch bis fotografisch (Tinte, Goldstaub, Horizont, Kohle, Leinen, Nebel, Dämmerung, Nachthimmel, Tusche, Papier, Aquarell) plus vier v2-Fassungen zur Abstimmung, alle 2048² JPEG mit 320er Vorschauen (7 MB statt 48 MB). Auswahl, Reihenfolge und Helligkeit stehen in src/config/backgrounds.js; der Picker zeigt Name und aktuelle Wahl, das Logo folgt der Bildhelligkeit, Presets zeigen auf die neuen Bilder, alte Pfade werden beim Laden auf den Nachfolger umgeschrieben. Der Service Worker cacht nur die Vorschauen vor. Werkzeuge zum Neu-Rendern (SVG-Builder, Gemini-Prompts) liegen in dev/tools/backgrounds, die Sichtungs-Renders bleiben lokal (gitignore). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
6.6 KiB
Google-Gemini-Bild-API: Zugang
Stand: 25.09.2026
Überblick
Die Key-Images der Demo-Lifelines entstehen über die Google Gemini API, Modell gemini-3.1-flash-image (Produktname „Nano Banana 2"). Der Zugang läuft ausschließlich über REST mit einem API-Key aus dem Google AI Studio – kein SDK, kein OAuth, kein Google-Cloud-Projekt-Setup nötig.
Genutzt wird der Zugang nur von den Entwickler-Werkzeugen unter dev/tools/lifeline-images/:
| Datei | Aufgabe |
|---|---|
gemini.py |
Einziger API-Client: liest den Key, baut den Request, speichert das Bild |
anchors.py |
Erzeugt die Altersanker je Figur (Porträt-Kette) |
events.py |
Erzeugt ein Bild pro Lifeline-Event mit Ankern als Referenz |
contact_sheet.py, montage.py |
Reine Sichtungs-Werkzeuge, ohne API-Zugriff |
Die Laravel-Anwendung selbst spricht nie mit der API. Sie importiert nur fertige Dateien per php artisan lifelines:import-images <handle>.
Zugangsdaten
Der Key steht als GOOGLE_API_KEY in backend/.env – sonst nirgends. .env ist in backend/.gitignore ausgeschlossen, der Key liegt also nicht im Repository.
GOOGLE_API_KEY=<key aus dem AI Studio>
gemini.py liest die Datei direkt zeilenweise und schneidet Anführungszeichen ab; fehlt der Eintrag, bricht das Skript mit GOOGLE_API_KEY missing in backend/.env ab. Der Key wird nur als HTTP-Header x-goog-api-key gesendet, nie in eine URL, einen Log oder eine Ausgabe geschrieben.
Regeln für den Umgang:
- Der Key gehört nur in die lokale Entwicklungsumgebung. Auf Produktion wird er nicht benötigt – dort landen ausschließlich die fertigen PNG-Dateien und der Import-Befehl.
- Generierte Bilder und Anker liegen unter
backend/storage/app/private/lifeline-images/und sind ebenfalls vom Repository ausgeschlossen. - Der Key ist ein Zahlungsmittel: Wer ihn hat, generiert auf Kosten des hinterlegten Guthabens. Nicht in Tickets, Chats oder Screenshots weitergeben.
- Bei Verdacht auf Weitergabe: im AI Studio löschen und neu erzeugen (siehe letzter Abschnitt).
Endpunkt und Request
Ein Aufruf ist ein POST auf generateContent des Bildmodells:
POST https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-image:generateContent
Content-Type: application/json
x-goog-api-key: <GOOGLE_API_KEY>
Der Body enthält einen einzigen Nutzer-Turn. Referenzbilder stehen als inline_data vor dem Text; ihre Reihenfolge ist bedeutsam, weil der Prompt sie als „Reference photo 1/2/3" anspricht:
{
"contents": [{ "role": "user", "parts": [
{ "inline_data": { "mime_type": "image/jpeg", "data": "<base64>" } },
{ "text": "<Prompt>" }
]}],
"generationConfig": {
"responseModalities": ["IMAGE"],
"imageConfig": { "aspectRatio": "4:3", "imageSize": "1K" }
}
}
Parameter, die wir setzen:
| Feld | Werte | Bedeutung |
|---|---|---|
responseModalities |
["IMAGE"] |
Nur Bild zurück, kein Begleittext |
imageConfig.aspectRatio |
4:3 bzw. 3:4 |
Querformat; 3:4 für Selfies und Porträt-Anker |
imageConfig.imageSize |
1K |
Auflösungsstufe |
| Modell-ID | Umgebungsvariable LIFELINE_IMAGE_MODEL |
Überschreibt die Vorgabe gemini-3.1-flash-image |
Einen seed verwenden wir nicht mehr; falls doch gesetzt, akzeptiert die API nur Werte bis 1.000.000.
Antwort verarbeiten
Das Bild steckt base64-kodiert im ersten Kandidaten:
candidates[0].content.parts[*].inlineData.data
Zwei Eigenheiten, die im Client berücksichtigt sind:
- Das Feld heißt je nach Antwort
inlineDataoderinline_data– beide Schreibweisen prüfen. - Die gelieferten Daten sind faktisch JPEG, obwohl wir die Dateien mit der Endung
.pngablegen. Beim Hochladen als Referenz wird der MIME-Typ deshalb aus den Magic Bytes bestimmt (\x89PNG→image/png, sonstimage/jpeg) und nicht aus der Dateiendung.
Kommt kein Bild zurück, steht der Grund in candidates[0].finishReason oder in promptFeedback. Der Client wiederholt den Aufruf bis zu dreimal (5 s Pause; bei HTTP 429/500/503 gestaffelt 10/20 s) und wirft danach einen Fehler mit dem Grund im Text.
Kontingente, Abrechnung, Fehlerbilder
Guthaben und Tageslimit sind zwei getrennte Grenzen. Beide haben wir beim Aufbau der Bildsets erreicht, sie sehen im Log sehr ähnlich aus:
| HTTP | Meldung | Ursache | Abhilfe |
|---|---|---|---|
| 402 | Your prepayment credits are depleted |
Prepaid-Guthaben aufgebraucht | Im AI Studio unter Billing aufladen; wirkt sofort |
| 429 | Quota exceeded for metric: generate_requests_per_model_per_day, limit: 100 |
Tageslimit von 100 Anfragen pro Modell erreicht | Warten bis Mitternacht Pacific (ca. 9 Uhr MESZ) oder höhere Stufe beantragen |
200 mit finishReason: IMAGE_OTHER |
The model could not generate the image based on the prompt provided |
Inhaltsfilter des Modells, deterministisch pro Prompt | Prompt umformulieren – Wiederholen hilft nicht |
Zum Tageslimit: Es zählt Anfragen, nicht erfolgreiche Bilder. Fehlversuche und Wiederholungen zählen mit. Ein vollständiger Durchlauf für eine große Lifeline (Jonas, 252 Bilder) sprengt das Limit deshalb und muss über mehrere Tage laufen. Der aktuelle Verbrauch ist unter ai.dev/rate-limit einsehbar.
Zu IMAGE_OTHER: Der Filter reagiert auf Formulierungen, nicht auf Zufall. In unseren Sets waren die Auslöser unter anderem Rollenangaben wie „Singer of the band" (Prominenten-Filter), oberkörperfreie Referenzbilder und Kombinationen aus erschöpften Gesichtsbeschreibungen mit Bettszenen. Vorgehen: Prompt halbieren und die Hälften einzeln testen, bis die auslösende Passage feststeht.
Key anlegen oder rotieren
-
aistudio.google.com mit dem Google-Konto öffnen, das die Rechnung trägt.
-
Unter Get API key einen Key für das gewünschte Projekt erzeugen. Der Wert wird nur einmal vollständig angezeigt.
-
Für das Bildmodell muss im Projekt Billing aktiv sein und Guthaben vorhanden – unter ai.studio/projects zu prüfen. Ohne Guthaben antwortet die API mit HTTP 402.
-
Key in
backend/.envalsGOOGLE_API_KEY=eintragen. Keinphp artisan config:clearnötig, da nur die Python-Werkzeuge ihn lesen. -
Prüfen mit einem einzelnen Bild, zum Beispiel:
cd dev/tools/lifeline-images python3 gemini.py "Ein roter Apfel auf einem weißen Tisch, Foto." --out /tmp/test.pngErfolg gibt den Pfad aus; bei Problemen erscheint die HTTP-Fehlermeldung im Klartext.
-
Alten Key nach der Umstellung im AI Studio löschen.
Das Tageslimit hängt an Projekt und Abrechnungsstufe, nicht am einzelnen Key. Ein neuer Key im selben Projekt umgeht das Limit also nicht.