> ## 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.

# Bonnes pratiques

> Mode de sondage, conception du prompt, image de référence, stratégie de relance en cas d'erreur, concurrence et conseils de dépannage pour l'intégration de Midjourney

Synthèse des bonnes pratiques pour les questions fréquentes, l'optimisation des performances et la gestion des erreurs. **À lire entièrement avant l'intégration.**

## Soumission de tâche et sondage

Les endpoints de soumission sont tous des tâches asynchrones : après la soumission, `task_id` est renvoyé, puis vous interrogez périodiquement `GET /v1/midjourney/{task_id}` pour récupérer le statut, jusqu'à `SUCCESS` / `FAILURE`.

```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} nécessite un appel à /modal pour compléter les paramètres")
        time.sleep(3)
    raise TimeoutError(task_id)
```

* **Rythme de sondage** : recommandé une fois toutes les 3 à 5 s ; une fréquence plus élevée est inutile et gaspille du quota.
* **Ne bloquez pas de manière synchrone** dans une requête web en attendant l'achèvement de la tâche : renvoyez `task_id` immédiatement après la soumission, et laissez le frontend sonder de manière asynchrone.

## Conception du prompt

**Un bon 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
```

* **Le sujet d'abord** : d'abord le sujet, puis la description de la scène, enfin les modificateurs.
* **Paramètres structurés explicites** : utiliser `--ar` / `--v` / `--s` (ou les champs body correspondants) est plus contrôlable que de s'appuyer sur les valeurs par défaut.
* **Évitez les termes ambigus** : `photorealistic` est plus précis que `realistic`.

**À éviter :** trop abstrait (« make it good »), sujet dispersé (plusieurs objets juxtaposés sans hiérarchie), mettre des mots entre guillemets (ils seront pris comme valeur littérale).

**Niji anime :** passez `niji: true` + `version: "7"` ; la plateforme normalise en `--niji 7`, et la facturation passe par `midjourney@imagine-niji7`.

## Bonnes pratiques pour l'image de référence

| Source                     | Pratique recommandée                                                                    | Remarque                                                                       |
| -------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Téléversement utilisateur  | Stockez d'abord sur votre propre OSS / CDN, puis passez cette URL lors de la soumission | Ne passez pas directement du base64 (gaspille de la bande passante)            |
| URL publique               | Passez directement                                                                      | Attention au SSRF (doit être accessible publiquement) et à la limite de 12 MiB |
| Tiers / autres productions | Re-stockez d'abord sur votre propre OSS                                                 | Les URL tierces peuvent expirer                                                |

* **Compressez à \< 5 MiB** : la limite de la plateforme est 12 MiB, mais les petites images se transmettent / traitent plus vite.
* Les formats PNG / JPG / WebP sont tous acceptés ; JPG haute qualité recommandé.
* Une résolution de 1024 à 2048 px suffit largement ; plus haut est du gaspillage.
* Poids de l'image de référence `iw` (0–3, défaut 1) : >1 colle davantage à l'original, \<1 plus libre.

## Gestion des erreurs et stratégie de relance

| code                  | Signification                                    | Stratégie de relance                                                                               |
| --------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `1` / `200`           | Succès                                           | ✅                                                                                                  |
| `4` VALIDATION\_ERROR | Erreur de paramètre                              | ❌ Ne pas relancer, corrigez le paramètre                                                           |
| `3` NOT\_FOUND        | Aucune instance disponible / task\_id inexistant | Si l'instance est indisponible, réessayez plus tard ; si le task\_id n'existe pas, ne relancez pas |
| `9` FAILURE           | Refus du service / erreur interne                | ⏳ Relançable, backoff exponentiel (1s, 4s, 16s)                                                    |
| `21` MODAL            | État non terminal                                | ✅ Continuez d'appeler `/modal`                                                                     |
| `24` BANNED\_PROMPT   | Mot sensible                                     | ❌ Ne pas relancer, modifiez le prompt ; **déjà remboursé automatiquement**                         |
| `429`                 | Limitation de débit                              | ⏳ Backoff exponentiel + jitter                                                                     |
| `5xx` / erreur réseau | Côté serveur / réseau                            | ⏳ Backoff exponentiel ; en cas d'erreur réseau, vous pouvez relancer immédiatement 1 fois          |

```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"])      # non relançable
            if data["code"] == 3 and "task" in data["description"]:
                raise ValueError(data["description"])      # task_id inexistant
            # les autres (9 / 429 / 5xx) sont relançables
        except httpx.RequestError:
            pass
        time.sleep((4 ** attempt) + random.uniform(0, 1))  # 1s / 4s / 16s ...
    raise RuntimeError(f"nombre maximal de relances atteint {max_attempts}")
```

## Flux d'opérations secondaires

```python theme={null}
# imagine → sondage → 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 composé localement, 1–2s
single_image = final["image_urls"][0]
```

Retouche locale (inpaint → modal en deux étapes) :

```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
# Le frontend dessine le masque (transparent = zone à repeindre), le téléverse sur votre propre OSS pour obtenir 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"])
```

> ⚠️ Une fois inpaint entré en MODAL, vous devez appeler `/modal` **dans les 30 minutes**, sinon le backend exécute automatiquement un CANCEL + remboursement.

## Contrôle de la facturation vidéo

* Segment unique : `batch_size: 1` → débite 1 × `midjourney@video`
* Lot de 4 segments : `batch_size: 4` → débite 4 × `midjourney@video`
* Segment unique HD : `video_type: "vid_1.1_i2v_720"` + `batch_size: 1` → débite 1 × `midjourney@video-720p`

**Recommandation** : si vous n'avez besoin que d'un segment pour la production, utilisez `batch_size=1` ; n'utilisez 4 que pour comparer plusieurs propositions, et n'activez pas 4 par défaut (coût multiplié par N).

## Concurrence et débit

```python theme={null}
import asyncio
sem = asyncio.Semaphore(10)  # au plus 10 soumissions concurrentes côté client

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

* La plateforme impose une limite au nombre de soumissions par minute ; au-delà, elle renvoie `429`, et il faut relancer avec backoff.
* La concurrence réelle de génération est déterminée par la capacité du système ; au-delà, les tâches sont mises en file d'attente ; une tâche bloquée longtemps en `SUBMITTED` est généralement en file d'attente.
* Le sondage doit impérativement inclure un `sleep` ; n'utilisez pas de boucle infinie sans sleep.

## Recommandations de surveillance

| Métrique                                 | Seuil de référence | Signification                                                                        |
| ---------------------------------------- | ------------------ | ------------------------------------------------------------------------------------ |
| Taux de SUCCESS des tâches (dernière 1h) | > 95%              | Un taux faible indique une anomalie du service / réseau                              |
| Durée moyenne d'achèvement               | \< 90s             | Une durée élevée indique une file d'attente                                          |
| Nombre de tâches bloquées en MODAL       | proche de 0        | Un nombre élevé indique que le client n'appelle pas `/modal`                         |
| Proportion de `code=24`                  | \< 5%              | Une proportion élevée indique que le prompt déclenche fréquemment des mots sensibles |

## Liste de contrôle de dépannage

| Symptôme                            | Piste de diagnostic                                                                                           |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Tâche longtemps en `SUBMITTED`      | Système en file d'attente, consultez plus tard                                                                |
| Tâche longtemps en `NOT_START`      | La plateforme effectuera automatiquement un timeout + remboursement plus tard, aucune action manuelle requise |
| Tâche `MODAL` au-delà de 30 minutes | Le client n'a pas appelé `/modal` ; elle a déjà été automatiquement CANCEL + remboursée                       |
| Champ `prompt` vide                 | Le résultat textuel d'une tâche describe se trouve dans le champ `description`                                |
| Une image manque dans `image_urls`  | La modération de contenu a bloqué une partie des images, consultez `fail_reason`                              |
| Facturation supérieure aux attentes | Consultez le champ `quota` ; pour la vidéo, n'oubliez pas × `batch_size`                                      |
