> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices

> Polling-Muster für die Midjourney-Anbindung, Prompt-Design, bildgesteuerte Generierung, Strategien für Fehler-Retries, Nebenläufigkeit und Fehlerbehebung

Zusammenfassung der Best Practices für häufige Probleme, Performance-Optimierung und Fehlerbehandlung, **vor der Anbindung empfohlen vollständig durchzulesen**.

## Aufgabenübermittlung und Polling

Die Übermittlungs-Endpunkte sind allesamt asynchrone Aufgaben: Nach dem Absenden wird eine `task_id` zurückgegeben; danach wird über `GET /v1/midjourney/{task_id}` periodisch der Status abgefragt, bis `SUCCESS` / `FAILURE` erreicht ist.

```python theme={null}
import time, httpx

def wait_task(task_id, timeout=300):
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = httpx.get(f"{HOST}/v1/midjourney/{task_id}",
                         headers={"Authorization": f"Bearer {API_KEY}"}).json()
        if resp["status"] in ("SUCCESS", "FAILURE"):
            return resp
        if resp["status"] == "MODAL":
            raise RuntimeError(f"task {task_id} muss über /modal vervollständigt werden")
        time.sleep(3)
    raise TimeoutError(task_id)
```

* **Polling-Takt**: Empfohlen alle 3–5 s; höhere Frequenz ist sinnlos und verschwendet Kontingent.
* **Nicht synchron im Web-Request blockieren**, bis die Aufgabe fertig ist – nach dem Absenden sofort die `task_id` zurückgeben und das Frontend asynchron pollen lassen.

## Prompt-Design

**Guter Prompt:**

```text theme={null}
a serene mountain lake at sunrise, photorealistic, soft golden light,
mist rising from water, snow-capped peaks in distance --ar 16:9 --v 8.1 --s 100
```

* **Motiv zuerst**: erst das Motiv, dann die Szene beschreiben, zuletzt die Modifikatoren.
* **Strukturierte Parameter explizit angeben**: `--ar` / `--v` / `--s` (oder die entsprechenden Body-Felder) zu verwenden ist kontrollierbarer als auf Standardwerte zu vertrauen.
* **Mehrdeutige Wörter vermeiden**: `photorealistic` ist eindeutiger als `realistic`.

**Vermeiden:** zu abstrakte Angaben („make it good"), zerstreute Motive (mehrere gleichrangige Objekte ohne klare Priorität), Wörter in Anführungszeichen setzen (werden als Literalwert interpretiert).

**Niji-Anime:** `niji: true` + `version: "7"` übergeben; die Plattform normalisiert dies zu `--niji 7`, die Abrechnung läuft über `midjourney@imagine-niji7`.

## Best Practices für bildgesteuerte Generierung

| Quelle                           | Empfohlene Vorgehensweise                                                | Hinweis                                                                |
| -------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| Benutzer-Upload                  | Zuerst im eigenen OSS / CDN speichern, beim Absenden diese URL übergeben | Nicht direkt base64 übergeben (verschwendet Bandbreite)                |
| Öffentliche URL                  | Direkt übergeben                                                         | SSRF beachten (muss öffentlich erreichbar sein) sowie das 12-MiB-Limit |
| Drittanbieter / andere Artefakte | Zuerst ins eigene OSS umlagern                                           | Drittanbieter-URLs können ablaufen                                     |

* **Auf \< 5 MiB komprimieren**: Das Plattform-Limit liegt bei 12 MiB, aber kleinere Bilder werden schneller übertragen und verarbeitet.
* Die Formate PNG / JPG / WebP sind alle möglich, empfohlen wird hochwertiges JPG.
* Eine Auflösung von 1024–2048 px reicht aus; höher ist Verschwendung.
* Bildgewicht `iw` (0–3, Standard 1): >1 näher am Original, \<1 freier.

## Fehlerbehandlung und Retry-Strategie

| code                   | Bedeutung                                           | Retry-Strategie                                                                                                |
| ---------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `1` / `200`            | Erfolg                                              | ✅                                                                                                              |
| `4` VALIDATION\_ERROR  | Parameterfehler                                     | ❌ Nicht erneut versuchen, Parameter korrigieren                                                                |
| `3` NOT\_FOUND         | Keine verfügbare Instanz / task\_id existiert nicht | Bei nicht verfügbarer Instanz später erneut versuchen; bei nicht existierender task\_id nicht erneut versuchen |
| `9` FAILURE            | Ablehnung durch den Dienst / interner Fehler        | ⏳ Erneut versuchbar, exponentielles Backoff (1s, 4s, 16s)                                                      |
| `21` MODAL             | Nicht-Endzustand                                    | ✅ Weiter `/modal` aufrufen                                                                                     |
| `24` BANNED\_PROMPT    | Sensibles Wort                                      | ❌ Nicht erneut versuchen, Prompt ändern; **bereits automatisch erstattet**                                     |
| `429`                  | Drosselung                                          | ⏳ Exponentielles Backoff + Jitter                                                                              |
| `5xx` / Netzwerkfehler | Serverseitig / Netzwerk                             | ⏳ Exponentielles Backoff; bei Netzwerkfehler 1-mal sofort erneut versuchbar                                    |

```python theme={null}
import time, random, httpx

def submit_with_retry(payload, max_attempts=5):
    for attempt in range(max_attempts):
        try:
            r = httpx.post(f"{HOST}/v1/midjourney/generations/imagine",
                           json=payload,
                           headers={"Authorization": f"Bearer {API_KEY}"},
                           timeout=30)
            data = r.json()
            if r.status_code == 200 and data["code"] in (1, 200):
                return data
            if data["code"] in (4, 24):
                raise ValueError(data["description"])      # nicht erneut versuchbar
            if data["code"] == 3 and "task" in data["description"]:
                raise ValueError(data["description"])      # task_id existiert nicht
            # die übrigen (9 / 429 / 5xx) sind erneut versuchbar
        except httpx.RequestError:
            pass
        time.sleep((4 ** attempt) + random.uniform(0, 1))  # 1s / 4s / 16s ...
    raise RuntimeError(f"maximale Anzahl an Wiederholungen erreicht {max_attempts}")
```

## Ablauf von Folgeoperationen

```python theme={null}
# imagine → Polling → upscale
imagine_id = submit({"prompt": "a cat"})["data"][0]["task_id"]
result = wait_task(imagine_id)           # grid_image_url + 4 image_urls + buttons
upscale_id = submit_to("/upscale", {"task_id": imagine_id, "index": 2})["data"][0]["task_id"]
final = wait_task(upscale_id)            # upscale lokal zusammengesetzt, 1–2s
single_image = final["image_urls"][0]
```

Lokales Neuzeichnen (inpaint → modal, zwei Schritte):

```python theme={null}
imagine_id = submit({"prompt": "a portrait"})["data"][0]["task_id"]; wait_task(imagine_id)
upscale_id = submit_to("/upscale", {"task_id": imagine_id, "index": 1})["data"][0]["task_id"]; wait_task(upscale_id)

inpaint_id = submit_to("/inpaint", {"task_id": upscale_id})["data"][0]["task_id"]  # status=modal
# Frontend zeichnet die Maske (transparent = Neuzeichnungsbereich), lädt sie ins eigene OSS hoch und erhält die mask_url
final = submit_to("/modal", {
    "task_id": inpaint_id,
    "prompt": "replace the eyes with cybernetic blue eyes",
    "mask_url": "https://your-oss.com/mask.png"
})
wait_task(final["data"][0]["task_id"])
```

> ⚠️ Nachdem inpaint in MODAL übergegangen ist, muss `/modal` **innerhalb von 30 Minuten** aufgerufen werden, sonst wird im Hintergrund automatisch CANCEL + Erstattung ausgelöst.

## Abrechnungssteuerung bei video

* Einzelclip: `batch_size: 1` → Abzug 1 × `midjourney@video`
* Stapel von 4 Clips: `batch_size: 4` → Abzug 4 × `midjourney@video`
* HD-Einzelclip: `video_type: "vid_1.1_i2v_720"` + `batch_size: 1` → Abzug 1 × `midjourney@video-720p`

**Empfehlung**: Wenn nur 1 Clip benötigt wird, `batch_size=1` verwenden; 4 nur für Stapel-Entwürfe nutzen und nicht standardmäßig auf 4 stellen (vervielfacht die Kosten um das N-fache).

## Nebenläufigkeit und Durchsatz

```python theme={null}
import asyncio
sem = asyncio.Semaphore(10)  # clientseitig maximal 10 gleichzeitige Übermittlungen

async def submit_one(prompt):
    async with sem:
        return await submit({"prompt": prompt})
```

* Die Plattform hat eine Obergrenze für die Anzahl der Übermittlungen pro Minute; bei Überschreitung wird `429` zurückgegeben und ein Retry mit Backoff ist nötig.
* Die tatsächliche Generierungs-Nebenläufigkeit wird durch die Systemkapazität bestimmt; bei Überschreitung wird die Aufgabe in eine Warteschlange gestellt; bleibt eine Aufgabe lange bei `SUBMITTED`, liegt das meist an der Warteschlange.
* Beim Polling unbedingt `sleep` einbauen; keine Endlosschleife ohne sleep.

## Monitoring-Empfehlungen

| Kennzahl                               | Referenzschwelle | Bedeutung                                                              |
| -------------------------------------- | ---------------- | ---------------------------------------------------------------------- |
| SUCCESS-Rate der Aufgaben (letzte 1 h) | > 95%            | Niedrig deutet auf Dienst-/Netzwerkanomalien hin                       |
| Durchschnittliche Fertigstellungsdauer | \< 90s           | Hoch deutet auf eine Warteschlange hin                                 |
| Anzahl in MODAL verweilender Aufgaben  | nahe 0           | Viele deuten darauf hin, dass der Client `/modal` nicht aufgerufen hat |
| Anteil `code=24`                       | \< 5%            | Hoch deutet darauf hin, dass der Prompt häufig sensible Wörter auslöst |

## Checkliste zur Fehlerbehebung

| Symptom                         | Untersuchungsrichtung                                                                                   |
| ------------------------------- | ------------------------------------------------------------------------------------------------------- |
| Aufgabe lange bei `SUBMITTED`   | System in der Warteschlange, später erneut abfragen                                                     |
| Aufgabe lange bei `NOT_START`   | Die Plattform löst später automatisch einen Timeout mit Erstattung aus, kein manuelles Eingreifen nötig |
| Aufgabe `MODAL` über 30 Minuten | Client hat `/modal` nicht aufgerufen, wurde bereits automatisch CANCEL + Erstattung                     |
| Feld `prompt` leer              | Das Textergebnis einer describe-Aufgabe steht im Feld `description`                                     |
| Ein Bild fehlt in `image_urls`  | Inhaltsprüfung hat einen Teil der Bilder blockiert, siehe `fail_reason`                                 |
| Abrechnung höher als erwartet   | Feld `quota` prüfen; bei video an × `batch_size` denken                                                 |
