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

# Melhores práticas

> Padrão de polling, design de prompt, imagem de referência, estratégia de retry de erros, concorrência e dicas de troubleshooting para integração com Midjourney

Reúne as melhores práticas sobre dúvidas comuns, otimização de desempenho e tratamento de erros. **Recomendamos ler tudo antes de integrar.**

## Submissão de tarefas e polling

Todos os endpoints de submissão são tarefas assíncronas: após a submissão, retornam um `task_id`, e então você consulta periodicamente `GET /v1/midjourney/{task_id}` para obter o status, até `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} precisa chamar /modal para completar os parâmetros")
        time.sleep(3)
    raise TimeoutError(task_id)
```

* **Ritmo do polling**: recomendamos a cada 3–5s; frequências maiores não têm sentido e desperdiçam cota.
* **Não bloqueie de forma síncrona dentro de uma requisição web** esperando a tarefa terminar —— retorne o `task_id` imediatamente após a submissão e deixe o frontend fazer polling assíncrono.

## Design de prompt

**Um bom 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
```

* **Sujeito primeiro**: primeiro o sujeito, depois a descrição da cena e por último os modificadores.
* **Parâmetros estruturados explícitos**: usar `--ar` / `--v` / `--s` (ou os campos correspondentes no body) é mais controlável do que depender dos valores padrão.
* **Evite palavras ambíguas**: `photorealistic` é mais claro do que `realistic`.

**Evite:** ser abstrato demais ("make it good"), sujeitos dispersos (vários objetos paralelos sem hierarquia), colocar palavras entre aspas (serão tratadas como valor literal).

**Anime Niji:** passe `niji: true` + `version: "7"`, a plataforma normaliza para `--niji 7`, e a cobrança vai por `midjourney@imagine-niji7`.

## Melhores práticas para imagem de referência

| Origem                       | Prática recomendada                                                   | Atenção                                                                 |
| ---------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Upload do usuário            | Salve primeiro no seu próprio OSS / CDN e envie essa URL na submissão | Não envie base64 diretamente (desperdiça banda)                         |
| URL pública                  | Envie diretamente                                                     | Atenção ao SSRF (deve ser acessível publicamente) e ao limite de 12 MiB |
| Terceiros / outros artefatos | Salve antes no seu próprio OSS                                        | URLs de terceiros podem expirar                                         |

* **Comprima para \< 5 MiB**: o limite da plataforma é 12 MiB, mas imagens menores são mais rápidas de transmitir / processar.
* Os formatos PNG / JPG / WebP são aceitos; recomendamos JPG de alta qualidade.
* Resolução de 1024–2048 px já é suficiente; mais que isso é desperdício.
* Peso da imagem de referência `iw` (0–3, padrão 1): >1 fica mais próximo da imagem original, \<1 fica mais livre.

## Tratamento de erros e estratégia de retry

| code                  | Significado                                         | Estratégia de retry                                                                              |
| --------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `1` / `200`           | Sucesso                                             | ✅                                                                                                |
| `4` VALIDATION\_ERROR | Parâmetro errado                                    | ❌ Não faça retry, corrija os parâmetros                                                          |
| `3` NOT\_FOUND        | Nenhuma instância disponível / task\_id inexistente | Se a instância estiver indisponível, tente mais tarde; se o task\_id não existir, não faça retry |
| `9` FAILURE           | Rejeição do serviço / erro interno                  | ⏳ Pode fazer retry, com backoff exponencial (1s, 4s, 16s)                                        |
| `21` MODAL            | Estado não terminal                                 | ✅ Continue chamando `/modal`                                                                     |
| `24` BANNED\_PROMPT   | Palavra sensível                                    | ❌ Não faça retry, altere o prompt; **reembolso já automático**                                   |
| `429`                 | Rate limit                                          | ⏳ Backoff exponencial + jitter                                                                   |
| `5xx` / erro de rede  | Servidor / rede                                     | ⏳ Backoff exponencial; em erro de rede, pode fazer retry imediato 1 vez                          |

```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"])      # não pode fazer retry
            if data["code"] == 3 and "task" in data["description"]:
                raise ValueError(data["description"])      # task_id inexistente
            # os demais (9 / 429 / 5xx) podem ter retry
        except httpx.RequestError:
            pass
        time.sleep((4 ** attempt) + random.uniform(0, 1))  # 1s / 4s / 16s ...
    raise RuntimeError(f"número máximo de tentativas atingido {max_attempts}")
```

## Fluxo de operações secundárias

```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 com composição local, 1–2s
single_image = final["image_urls"][0]
```

Redesenho local (inpaint → modal, dois passos):

```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
# o frontend desenha a mask (transparente=área a redesenhar), faz upload no seu próprio OSS e obtém a 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"])
```

> ⚠️ Depois que o inpaint entra em MODAL, é obrigatório chamar `/modal` **dentro de 30 minutos**, caso contrário o backend faz CANCEL automático + reembolso.

## Controle de cobrança de video

* Trecho único: `batch_size: 1` → cobra 1 × `midjourney@video`
* Lote de 4 trechos: `batch_size: 4` → cobra 4 × `midjourney@video`
* Trecho único em HD: `video_type: "vid_1.1_i2v_720"` + `batch_size: 1` → cobra 1 × `midjourney@video-720p`

**Recomendação**: se só precisa de 1 trecho final, use `batch_size=1`; só use 4 para comparar variações em lote. Não deixe 4 como padrão (o custo multiplica por N).

## Concorrência e throughput

```python theme={null}
import asyncio
sem = asyncio.Semaphore(10)  # no máximo 10 submissões concorrentes no cliente

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

* A plataforma tem um limite de submissões por minuto; ao exceder, retorna `429` e é necessário retry com backoff.
* A concorrência real de geração é determinada pela capacidade do sistema; ao exceder, entra em fila; uma tarefa parada por muito tempo em `SUBMITTED` geralmente significa que está na fila.
* O polling deve sempre incluir `sleep`; não faça loop infinito sem sleep.

## Recomendações de monitoramento

| Métrica                                 | Limite de referência | Significado                                                                   |
| --------------------------------------- | -------------------- | ----------------------------------------------------------------------------- |
| Taxa de SUCCESS das tarefas (última 1h) | > 95%                | Se estiver baixa, indica anomalia no serviço / rede                           |
| Tempo médio de conclusão                | \< 90s               | Se estiver alto, indica fila                                                  |
| Número de tarefas paradas em MODAL      | Próximo de 0         | Se estiver alto, indica que o cliente não chamou `/modal`                     |
| Proporção de `code=24`                  | \< 5%                | Se estiver alta, indica que o prompt aciona palavras sensíveis com frequência |

## Checklist de troubleshooting

| Sintoma                                      | Direção de investigação                                                                   |
| -------------------------------------------- | ----------------------------------------------------------------------------------------- |
| Tarefa parada em `SUBMITTED` por muito tempo | Sistema em fila, consulte mais tarde                                                      |
| Tarefa parada em `NOT_START` por muito tempo | A plataforma fará timeout automático e reembolso mais tarde, não é necessária ação manual |
| Tarefa em `MODAL` por mais de 30 minutos     | O cliente não chamou `/modal`; já foi feito CANCEL automático + reembolso                 |
| Campo `prompt` vazio                         | O resultado em texto de uma tarefa describe fica no campo `description`                   |
| Falta uma imagem em `image_urls`             | A moderação de conteúdo bloqueou parte das imagens, veja `fail_reason`                    |
| Cobrança acima do esperado                   | Veja o campo `quota`; em video, lembre de multiplicar por `batch_size`                    |
