# Modalities — Image Generation (media-generation)

> **Scope:** stacks=["dotnet"]
> **Layer:** 2 (on-keyword)
> **Keywords:** image generation, text to image, gpt-image, nano banana, image editing, product photo, mockup generation, image synthesis
> **Read by Claude in:** implement (quando o projeto precisa gerar ou editar imagens em runtime)

**Verified against:** OpenAI 2.13.0 + Microsoft.Extensions.AI 10.9.0 + Google.GenAI 1.21.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs` — `IImageGenerator`, `MEAI001`, `ImageGenerationRequest.OriginalImages` e a ausência de propriedade tipada para `quality` COMPILAM ali, e `OPENAI001` em `GeneratedImageQuality` foi medido pelo compilador. `HostedImageGenerationTool` foi medido por reflexão sobre a assembly do pin (existe, não é experimental); preço, data de desligamento e limites de referência são **verificação documental** contra as páginas de §Como re-verificar este standard. Last-verified: 2026-09-08.

---

Geração de imagem **em runtime, a partir de .NET**. Autoria de asset de landing page por MCP, em
tempo de design, é outro standard e outro consumidor:
`frontend/design-system/ai-image-generation.md` — prompt craft de marca, `assets.md`, integração com
`next/image`. Este aqui é sobre a chamada paga que o seu backend faz, o que ela custa e onde ela
pode morar.

O pipeline que recebe o pedido, revisa e publica está em `ai-agents/media-pipeline.md`. Vídeo está
em `ai-agents/media-video.md`.

---

## Quando usar geração de imagem

| Caso | Por que a geração ganha do asset estático |
|---|---|
| **Catálogo** | Variação de tecido, cor e cena sobre o **mesmo** produto real, a partir de foto de referência |
| **Marketing** | Peça de campanha por segmento, com texto na imagem, em volume |
| **Variação de produto** | N combinações que ninguém fotografaria uma a uma |

**Não use** quando um asset estático resolve: custo por chamada, latência de vários segundos e
nenhuma garantia de repetibilidade. Se a imagem não muda por usuário ou por contexto, sirva de CDN.

**Não use dentro de um turno de conversa.** Esta é a regra dura da casa, e ela não é estética:
a documentação da própria OpenAI declara que um prompt complexo pode levar **até 2 minutos**, cada
chamada custa dinheiro, e dentro do turno **não existe revisão humana possível**. O turno enfileira
e confirma; a mídia volta depois, revisada. O mecanismo que prende essa regra — e não apenas a
explica — está em `ai-agents/media-pipeline.md`.

---

## A API

Duas superfícies, e a escolha entre elas é por **necessidade**, não por gosto.

### Abstração `IImageGenerator` (Microsoft.Extensions.AI)

```csharp
using Microsoft.Extensions.AI;
using OpenAI.Images;

#pragma warning disable MEAI001 // IImageGenerator e experimental: a superficie pode mudar entre menores
// AsIImageGenerator() vem de Microsoft.Extensions.AI.OpenAI — um pacote A MAIS.
// Sem ele, escreva o adaptador (e o kit escreve: ver a nota abaixo).
IImageGenerator generator = new ImageClient("gpt-image-2", apiKey).AsIImageGenerator();

var response = await generator.GenerateAsync(
    new ImageGenerationRequest { Prompt = prompt },
    new ImageGenerationOptions { MediaType = "image/png", Count = 1 },
    ct);
#pragma warning restore MEAI001

var bytes = response.Contents.OfType<DataContent>().First().Data;
```

> **A ponte é um pacote separado, e isso muda o seu `.csproj`.** `AsIImageGenerator()` é uma extensão
> de `Microsoft.Extensions.AI.OpenAI`; ter `Microsoft.Extensions.AI` e `OpenAI` **não** basta —
> medido pelo compilador. Se o pacote da ponte não estiver no seu pin, o caminho é escrever um
> `IImageGenerator` sobre o `ImageClient`, como o exemplar do kit faz em
> `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs`. As duas
> alternativas são legítimas; o que não é legítimo é descobrir isso em tempo de build depois de ter
> escrito o pipeline inteiro.

Fatos que decidem o desenho, medidos contra `Microsoft.Extensions.AI.Abstractions` 10.9.0:

- `IImageGenerator` carrega `[Experimental("MEAI001")]`. Suprimir o diagnóstico é obrigatório, e o
  `#pragma` **leva comentário do risco** — é o que separa "assumimos a instabilidade" de
  "silenciamos um aviso".
- `ImageGenerationRequest` tem `Prompt` e `OriginalImages` (`IEnumerable<AIContent>`). `OriginalImages`
  é o gancho de **edição com referência** — é por ele que o produto real é preservado.
- `ImageGenerationOptions` expõe `AdditionalProperties`, `Count`, `ImageSize`, `MediaType`,
  `ModelId`, `RawRepresentationFactory`, `ResponseFormat` e `StreamingCount`. **Não há propriedade
  tipada** para `quality`, `background` ou `input_fidelity`: esses vão por `AdditionalProperties` ou
  por `RawRepresentationFactory`. Escrever `options.Quality` não compila, e procurar por ela é o
  primeiro tropeço de quem chega vindo do `ImageClient` direto.
- Único adaptador oficial é OpenAI/Azure OpenAI (`OpenAIClientExtensions.AsIImageGenerator`, **no
  pacote `Microsoft.Extensions.AI.OpenAI` — ver a ressalva acima**). Para
  Gemini **não existe** adaptador MEAI oficial: use `Google.GenAI` direto
  (`Client.Models.GenerateContentAsync` com `ResponseModalities = ["IMAGE", "TEXT"]`, lendo
  `Candidates[0].Content.Parts[..].InlineData`), ou escreva um `IImageGenerator` próprio.

### Images API direto, quando o produto real precisa ser preservado

`POST /v1/images/edits` aceita N imagens de referência (PNG/JPG/WebP, cada uma **abaixo de 50 MB**,
mesmo formato e tamanho) e uma `mask` PNG com alfa nas mesmas dimensões da primeira imagem. É o
caminho para catálogo: editar preserva; gerar do zero recria.

`GenerateImageVariationAsync` do SDK **não é opção** — a API de variações só existia no modelo
listado em §Deprecações.

---

## Tool hospedada de imagem

`Microsoft.Extensions.AI.HostedImageGenerationTool` existe na 10.9.0, **não** é experimental e é um
*marker*: ela não gera nada, apenas informa ao serviço que o modelo pode gerar
(`Name => "image_generation"`). O adaptador da OpenAI mapeia `ModelId → Model`,
`MediaType → OutputFileFormat`, `StreamingCount → PartialImageCount` e `ImageSize → Size`;
**`quality`, `background`, `moderation` e `input_fidelity` não são mapeados**. O resultado volta
como `ImageGenerationToolResultContent { Outputs = [DataContent] }` dentro de
`ChatResponse.Messages[..].Contents` — nunca como `FunctionResultContent`.

> **Procedência, separada em dois níveis.** Que o tipo **existe** e **não** é experimental na 10.9.0
> foi medido por reflexão sobre a assembly do pin. Já a **tabela de mapeamento** acima vem da leitura
> de `OpenAIResponsesChatClient.cs` em
> `https://github.com/dotnet/extensions/blob/main/src/Libraries/Microsoft.Extensions.AI.OpenAI/`,
> consultado em 2026-09-07 — é
> código **interno** da ponte, fora da superfície pública, e portanto não confirmável por reflexão.
> Confirme contra o provedor configurado antes de contar com um desses parâmetros pela tool.

**E é exatamente por isso que ela não entra no agente de WhatsApp.** A tool é o caminho natural de
"me mostra esse sofá em verde", e a documentação a descreve para chat/atendimento. Este standard
**contradiz** essa leitura para o atendimento, com o motivo escrito:

| Fato | Consequência no turno |
|---|---|
| Latência documentada de até 2 min | O turno estoura; o cliente vê silêncio |
| Custo por chamada, sem teto natural | Um usuário curioso vira uma fatura |
| `quality`/`moderation` não mapeados pelo adaptador | Você não controla nem o custo nem o filtro |
| Nenhuma revisão possível dentro do turno | Claim visual errado sai direto para o cliente |

O que o turno faz é **enfileirar e confirmar**. A tool hospedada é legítima em ferramenta interna,
com humano na frente da tela e orçamento controlado — nunca no canal do cliente.

---

## Provedor por alias

O alias mora no `model-registry.v2.json` do kit, com `api` declarando o protocolo. Alias de imagem
usa `api: images`; alias de vídeo, `api: videos` (ver `ai-agents/media-video.md`). O `AgentFactory`
**recusa com erro nomeado** um alias de mídia passado como agente de chat: um alias de imagem
resolvido como conversa apontaria para um modelo que não fala chat, e a falha apareceria tarde e
confusa.

| Papel | Alias | Por quê |
|---|---|---|
| Imagem, default | `gpt-image-2` | Default do próprio provedor; resolução arbitrária (múltiplos de 16 px, lado ≤ 3840 px, razão ≤ 3:1); até 8 imagens coerentes por prompt; `input_fidelity` deixou de existir porque a fidelidade já é alta |
| Imagem, referência de produto | `gemini-3.1-flash-image` | 10 objetos + 4 personagens + 3 estilos de referência — o caso "muitas fotos do mesmo móvel" |
| Imagem, lote barato | `gemini-3.1-flash-lite-image` | O piso de preço da família, com 14 objetos de referência |
| Vídeo | ver `ai-agents/media-video.md` | Fora do escopo deste standard |

**Nenhum alias fora desta lista sem passar pela tabela de §Deprecações primeiro.** Um alias
proibido não é preferência de estilo: é dinheiro gasto num endpoint que vai devolver erro.

---

## Prompt como spec versionada

Um prompt de produção é **template versionado**, nunca string concatenada no handler. O que o
template fixa:

1. **Estrutura, sempre na mesma ordem:** cena/fundo → sujeito → detalhes-chave → restrições.
   Segmentos rotulados; frases descritivas, não lista de keywords.
2. **Fotorrealismo declarado** quando é o caso ("photorealistic", "professional product
   photography"); termos de câmera servem à composição, não à física.
3. **Texto na imagem entre aspas literais**, marcas soletradas.
4. **"Preserve list" repetida em CADA iteração de edição** — "change only X, keep everything else".
   Iterações pequenas e sequenciais; ao driftar, reinicie a conversa com a descrição completa em vez
   de empilhar correções.
5. **Fundo transparente vem do parâmetro, não do prompt** (`background=transparent` + PNG/WebP):
   descrever o fundo no texto faz o prompt vencer o parâmetro.

O prompt final vai para a proveniência do job junto com o `revised_prompt` que o provedor devolve —
ver `ai-agents/media-pipeline.md`. Prompt que não foi gravado não pode ser reproduzido nem auditado.

Para prompt craft de marca (paleta, tom, herói aprovado como referência das derivadas), o standard
é `frontend/design-system/ai-image-generation.md`; este não o repete.

---

## Referências de produto

| Modelo | Limite de referência |
|---|---|
| `gemini-3.1-flash-lite-image` | 14 objetos |
| `gemini-3.1-flash-image` | 10 objetos + 4 personagens + 3 estilos |
| `gemini-3-pro-image` | 6 objetos + 5 personagens |
| OpenAI `/v1/images/edits` | N imagens, cada uma abaixo de 50 MB, mesmo formato e tamanho |

**Indexe cada referência no prompt** ("image 1 = o sofá; image 2 = o tecido") e diga como elas
interagem. Referência não nomeada é referência que o modelo escolhe sozinho como usar.

O hash sha256 de cada referência entra na chave de idempotência do pedido (`media-pipeline.md`):
trocar a foto de referência **é** outro pedido, mesmo com o prompt idêntico.

---

## Exemplo C# completo

```csharp
// SEM `using OpenAI.Images;`. Ele nao e necessario aqui — o gerador chega injetado e nenhum
// tipo do SDK da OpenAI aparece neste arquivo — e deixa-lo torna `ImageGenerationOptions`
// AMBIGUO (CS0104) entre Microsoft.Extensions.AI e OpenAI.Images. Onde os dois convivem de
// verdade, como no adaptador do kit, o tipo vai QUALIFICADO.
using Microsoft.Extensions.AI;

// O pragma abre ACIMA da classe, e nao dentro do metodo: o parametro do construtor primario
// tambem nomeia IImageGenerator, e MEAI001 e ERRO por padrao — nao aviso. Pragma dentro do
// corpo deixa a assinatura de fora e o arquivo nao compila.
#pragma warning disable MEAI001 // IImageGenerator/ImageGenerationRequest sao experimentais: a superficie pode mudar entre menores.

/// <summary>
/// Adaptador de PROVIDER. Note o que ele NAO e: um servico publico que um handler
/// possa chamar. Quem produz bytes vive atras do enfileiramento descrito em
/// ai-agents/media-pipeline.md, e no kit esse tipo e `internal` de proposito.
/// </summary>
/// <remarks>
/// O IImageGenerator chega INJETADO, e nao construido aqui. As duas formas de obte-lo
/// sao legitimas e a escolha e do seu pin (ver a ressalva da secao "A API"):
///   - com Microsoft.Extensions.AI.OpenAI no pin:  imageClient.AsIImageGenerator()
///   - sem ele: um IImageGenerator escrito a mao sobre o ImageClient, como em
///     templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs
/// Injetar em vez de construir e o que faz ESTE exemplo compilar nos dois casos.
/// </remarks>
internal sealed class OpenAiImageAdapter(IImageGenerator generator)
{
    // ReadOnlyMemory<byte> dos dois lados porque e o tipo REAL de DataContent.Data.
    // Declarar BinaryData aqui nao converte (CS0029) — e o compilador que decide isso.
    public async Task<ReadOnlyMemory<byte>> ProduceAsync(
        string prompt,
        IReadOnlyList<ReadOnlyMemory<byte>> references,
        CancellationToken ct)
    {
        var request = new ImageGenerationRequest { Prompt = prompt };
        if (references.Count > 0)
        {
            // OriginalImages e o gancho de EDICAO: preserva o produto real em vez de recria-lo.
            request.OriginalImages = [.. references.Select(r => new DataContent(r, "image/png"))];
        }

        var options = new ImageGenerationOptions
        {
            MediaType = "image/png",
            Count = 1,
            // Nao ha propriedade tipada para quality/background/input_fidelity na 10.9.0.
            AdditionalProperties = new() { ["quality"] = "low" },
        };

        var response = await generator.GenerateAsync(request, options, ct);

        return response.Contents.OfType<DataContent>().First().Data;
    }
}

#pragma warning restore MEAI001
```

**Escada de qualidade, e ela é obrigatória:** gere N candidatas em `low`, escolha com humano, e só
então regenere a vencedora em `high`. A conta de gerar tudo em `high` está em §Custo por chamada.

---

## Parâmetros

> Superfície de `ImageGenerationOptions` medida contra `Microsoft.Extensions.AI.Abstractions`
> 10.9.0; parâmetros de wire medidos contra a Images API da OpenAI.

| Parâmetro | Onde vive | Nota |
|---|---|---|
| `ModelId` | tipado | Vem do alias, nunca do handler |
| `Count` | tipado | N candidatas numa chamada; cobra por imagem |
| `ImageSize` | tipado (`System.Drawing.Size`) | `gpt-image-2` aceita múltiplos de 16 px até 3840 px de lado |
| `MediaType` | tipado | `image/png`, `image/jpeg`, `image/webp` |
| `ResponseFormat` | tipado | Prefira bytes. URL de provedor expira — ver §Deprecações e `media-pipeline.md` |
| `StreamingCount` | tipado | Imagens parciais (0-3) para feedback de UI interna |
| `quality` | `AdditionalProperties` | `low`/`medium`/`high`/`auto`. **Não é tipado na 10.9.0** |
| `background` | `AdditionalProperties` | `transparent`/`opaque`/`auto`; exige PNG ou WebP |
| `moderation` | `AdditionalProperties` | `auto`/`low`. Bloqueio é `rejected_by_policy`, não erro técnico |
| `output_compression` | `AdditionalProperties` | 0-100, só JPEG/WebP |
| `mask` | só Images API direto | PNG com alfa, mesmas dimensões da primeira referência |

Nada em `AdditionalProperties` é validado em compilação. Um erro de digitação ali é um parâmetro que
o provedor ignora em silêncio — cubra com teste de contrato o mapa nome→valor que o seu adaptador
monta.

---

## Custo por chamada

> **Toda a aritmética de dinheiro deste standard mora nesta seção, e em lugar nenhum mais.**
> Re-verificar preço é reabrir esta seção e §Deprecações — não reler o standard inteiro.

Preço de **lista pública**, em USD, sem desconto de contrato. Consultadas em **2026-09-07**:
`https://developers.openai.com/api/docs/pricing` e `https://ai.google.dev/gemini-api/docs/pricing`.

| Item | Preço | Origem |
|---|---|---|
| `gpt-image-2` — token de imagem de saída | $30,00 / 1M | OpenAI pricing (lista) |
| `gpt-image-2` — 1024², low / medium / high | ≈ $0,006 / $0,053 / $0,211 | **Estimativa de terceiro**, derivada da calculadora (`https://wavespeed.ai/blog/posts/gpt-image-2-pricing-2026/`). **Não é preço de lista** — a OpenAI publica por token |
| `gemini-3.1-flash-lite-image` | $0,0336 (Batch = Standard) | Gemini pricing |
| `gemini-3.1-flash-image` — 1K | $0,067 (Batch $0,034) | Gemini pricing |
| `gemini-3-pro-image` — 1K/2K | $0,134 (Batch $0,067) | Gemini pricing |
| Batch API (OpenAI e Google) | −50 % | páginas de pricing acima |

**A ressalva da segunda linha é parte do standard, não rodapé.** O preço por imagem do `gpt-image-2`
não tem fonte primária: a única tarifa oficial é por token. Um número derivado por terceiro serve
para dimensionar teto de orçamento, nunca para faturar cliente.

Três consequências de desenho, não de opinião:

1. **Escada de qualidade.** A diferença entre `low` e `high` na estimativa acima é de mais de 30×.
   Candidatas em `low`; `high` só na aprovada.
2. **Batch para catálogo.** Metade do preço em lote é a maior alavanca isolada disponível. A decisão
   Batch × Flex × síncrono já está escrita: `ai-agents/batching.md` e `ai-agents/service-tier-flex.md`.
3. **Custo estimado gravado ANTES da chamada**, e teto por tenant/dia em unidades (imagens ×
   qualidade). O mecanismo está em `ai-agents/media-pipeline.md`; a tarifa está aqui.

Token de imagem de **entrada** (edição com referência) também é cobrado, e `input_fidelity: high`
nos modelos legados aumenta a fatura. Edição não é mais barata que geração por definição.

---

## Deprecações

> **Toda data de desligamento deste standard mora nesta seção, e em lugar nenhum mais.**

Fontes, consultadas em **2026-09-07**: `https://developers.openai.com/api/docs/deprecations` e
`https://ai.google.dev/gemini-api/docs/imagen` (mais o changelog em
`https://ai.google.dev/gemini-api/docs/changelog`).

| Alias | Situação | Data |
|---|---|---|
| `gpt-image-1`, `gpt-image-1-mini`, `gpt-image-1.5`, `chatgpt-image-latest` | **desligam** (anunciado 2026-06-02) | 2026-12-01 |
| `dall-e-2`, `dall-e-3` | **desligados** | 2026-05-12 |
| `imagen-4.0-generate-001` e irmãos | **desligado** (anunciado 2026-06-15) | 2026-08-17 |
| `gemini-2.5-flash-image` | legado, migrar — sem data anunciada | — |
| `sora-2`, `sora-2-pro` (vídeo) | **desligam, sem substituto** | 2026-09-24 — ver `ai-agents/media-video.md` |

**Próximo desligamento conhecido: 2026-09-24.** Se esta data já passou quando você está lendo, este
standard não está apenas velho — ele está **errado**, e a §Como re-verificar diz em qual página
conferir. Uma data no passado nesta linha é falsificação em segundos, sem pesquisa.

O alias `dall-e` continua na busca do `STANDARDS.json` de propósito: quem procurar pelo modelo morto
tem de cair **aqui**, no lugar que diz que ele morreu.

---

## Anti-patterns

| Anti-pattern | Por quê é errado | Jeito certo |
|---|---|---|
| Gerar imagem quando asset estático resolve | Custo e latência sem ganho | Sirva de CDN |
| **Gerar dentro do turno de conversa** | Latência de até 2 min, custo por mensagem, zero revisão | Enfileire e confirme (`media-pipeline.md`) |
| **Usar alias em lista de desligamento** | Dinheiro gasto num endpoint com data de morte | Confira §Deprecações antes de pinar |
| **Gravar a URL do provedor em vez dos bytes** | URL de provedor expira; o link quebra depois | Baixe, grave no blob próprio, guarde caminho + sha256 |
| **Gerar do zero quando o produto real existe** | Inventa material, medida ou cor que o produto não tem — claim visual falso | Edição com `OriginalImages` / `/v1/images/edits` |
| Não moderar o prompt antes de enviar | Erro 400 sem log útil e conteúdo impróprio | Valide antes; ver `ai-agents/middleware-patterns.md` |
| Criar `AIAgent` para geração de imagem | `IImageGenerator` não é `IChatClient` | Serviço enfileirado, não agente |
| Suprimir `MEAI001` sem comentário | Silencia o aviso e esconde o risco de breaking change | `#pragma` com o motivo escrito na linha |

---

## Checklist (verifiable by morph-eval)

- [ ] O alias escolhido **não** está na tabela de §Deprecações
- [ ] A chamada passa por `IImageGenerator` (ou adaptador próprio), sem acoplar o handler ao SDK
- [ ] `MEAI001` suprimido explicitamente, com comentário do risco na mesma linha
- [ ] Edição com referência usa `OriginalImages` / `/v1/images/edits` quando o produto real existe
- [ ] Prompt montado por template versionado, com a estrutura fixa e a "preserve list" repetida
- [ ] `quality` mínimo por finalidade — candidatas em `low`, `high` só na aprovada
- [ ] Custo estimado gravado **antes** da chamada, com a tarifa de §Custo por chamada
- [ ] Bytes gravados em blob próprio; nenhuma URL de provedor persistida como se fosse permanente
- [ ] Nenhuma geração dentro de um turno de conversa — o handler devolve id de job
- [ ] Nenhum teste toca API paga

---

## Como re-verificar este standard

Cinco páginas, cinco perguntas. Cabe numa sessão; é o custo que torna o corte de 90 dias cumprível
em vez de desativado por allowlist.

| Página | O que conferir |
|---|---|
| `https://developers.openai.com/api/docs/deprecations` | A tabela de §Deprecações inteira, e a linha "próximo desligamento conhecido" |
| `https://developers.openai.com/api/docs/pricing` | O preço por token de saída de imagem, e se apareceu preço por imagem oficial (hoje não há) |
| `https://ai.google.dev/gemini-api/docs/pricing` | As três linhas Gemini de §Custo por chamada |
| `https://ai.google.dev/gemini-api/docs/image-generation` | Limites de referência da §Referências de produto |
| `https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.ai.imagegenerationoptions` + `https://www.nuget.org/packages/Microsoft.Extensions.AI` | Se `quality`/`background` ganharam propriedade tipada, e se `MEAI001` saiu do experimental |

**E o exemplo, que é uma afirmação verificável, se re-verifica compilando — não lendo:**

```bash
node scripts/extract-doc-csharp.mjs \
  framework/standards/ai-agents/modalities-image-gen.md \
  "## Exemplo C# completo" /tmp/probe/Exemplo.cs
cd /tmp/probe && dotnet build     # csproj com o pin + TreatWarningsAsErrors
```

O `TreatWarningsAsErrors` é obrigatório na sonda: sem ele `MEAI001` vira aviso e o build fica verde
sobre um exemplo que o consumidor **não** consegue compilar. Este bloco foi medido nos dois
cenários do pin — com e sem `Microsoft.Extensions.AI.OpenAI` — e sai `0 Aviso(s), 0 Erro(s)` nos
dois. Antes disso ele carregava três erros independentes, nenhum ligado ao pacote-ponte.

Ao terminar, atualize `Last-verified` **e** o `ai-pin` citado no header — re-datar sem re-verificar é
o defeito que `scripts/check-freshness.js` existe para impedir.

---

## References

- `ai-agents/media-pipeline.md` — o job, a revisão humana, a marcação e a regra dura
- `ai-agents/media-video.md` — vídeo em runtime, e o desligamento da Sora
- `frontend/design-system/ai-image-generation.md` — autoria de asset de LP via MCP: prompt craft de
  marca, `assets.md`, `next/image`. Consumidor diferente, tempo diferente
- `ai-agents/batching.md` · `ai-agents/service-tier-flex.md` — a decisão Batch × Flex × síncrono
- `ai-agents/providers/model-registry.md` — o alias por provedor
- `ai-agents/setup.md` — pacotes e DI

---

*MORPH-SPEC by Polymorphism Tech — ai-agents/modalities-image-gen.md v2.0*
