Biblioteca Java para integrar operações de image-to-image a modelos de inteligência artificial integrando ao módulo existente do Spring AI.
A proposta do projeto é receber uma imagem e instruções textuais, delegar a edição a um provedor de IA e devolver o resultado usando as abstrações de imagem do Spring AI. A implementação atual integra a API de edição de imagens da OpenAI; a separação entre core e provider prepara a biblioteca para receber outros provedores no futuro.
O projeto está em desenvolvimento e usa a versão
0.1.0-SNAPSHOT. A API pública ainda pode mudar antes da primeira versão estável.
- Java 21 ou superior
- Spring Framework 7
- Spring AI 2
- Uma chave de API da OpenAI
Enquanto o artefato não estiver publicado em um repositório Maven, clone o projeto e instale o snapshot localmente:
mvn clean installDepois, adicione a dependência ao projeto consumidor:
<dependency>
<groupId>io.github.NicolasBrum</groupId>
<artifactId>ai-image-edit</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>O fluxo principal da biblioteca é:
imagem + instruções + opções
|
v
AiImageEditPrompt
|
v
AiImageEditModel
|
v
cliente do provedor de IA
|
v
Spring AI ImageResponse
As responsabilidades estão separadas da seguinte forma:
AiImageEditPromptrepresenta a imagem, as instruções e as opções da edição.AiImageEditModelé a API usada pela aplicação para solicitar uma edição.AiImageEditClientdefine o contrato comum dos clientes de provedores.OpenAiImageEditClienttransforma o prompt em uma requisição multipart para um modelo específico de IA.OpenAiImageResultAdapterconverte a resposta específica do modelo para o modelo comum da biblioteca.
A chamada é síncrona: AiImageEditModel.call(...) aguarda o provedor terminar a edição e retorna um ImageResponse do Spring AI.
A configuração fornecida pela biblioteca registra um OpenAiImageEditClient e um AiImageEditModel. Importe-a na aplicação:
import io.github.nicolasbrum.config.OpenAiImageEditConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(OpenAiImageEditConfiguration.class)
public class ImageEditConfiguration {
}Configure a chave e, se necessário, os timeouts:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
ai:
image:
edit:
client:
openai:
connection-timeout: PT30S
read-timeout: PT3MA chave também pode ser configurada em ai.image.edit.client.openai.api-key. Quando as duas propriedades existem, spring.ai.openai.api-key tem prioridade.
Os timeouts usam o formato ISO-8601 de java.time.Duration. Exemplos: PT15S para 15 segundos e PT2M para 2 minutos.
Com o modelo registrado, construa o prompt e faça a chamada:
import io.github.nicolasbrum.core.AiImageEditModel;
import io.github.nicolasbrum.core.AiImageEditPrompt;
import org.springframework.ai.image.ImageOptions;
import org.springframework.ai.image.ImageOptionsBuilder;
import org.springframework.ai.image.ImageResponse;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
@Service
public class ProductImageService {
private final AiImageEditModel imageEditModel;
public ProductImageService(AiImageEditModel imageEditModel) {
this.imageEditModel = imageEditModel;
}
public ImageResponse removeBackground(String imagePath) {
ImageOptions options = ImageOptionsBuilder.builder()
.model("gpt-image-1")
.n(1)
.build();
AiImageEditPrompt prompt = AiImageEditPrompt.builder()
.resource(new FileSystemResource(imagePath))
.mediaType(MediaType.IMAGE_PNG)
.instruction("Remova o fundo da imagem.")
.instruction("Preserve o produto e suas cores originais.")
.imageOptions(options)
.build();
return imageEditModel.call(prompt);
}
}O MediaType deve representar o conteúdo real do arquivo enviado. Por exemplo, use MediaType.IMAGE_JPEG para uma imagem JPEG.
O cliente também pode ser construído diretamente. Isso é útil em aplicações que não usam um contexto Spring ou que preferem controlar a criação dos objetos:
import io.github.nicolasbrum.core.AiImageEditModel;
import io.github.nicolasbrum.core.AiImageEditPrompt;
import io.github.nicolasbrum.core.DefaultAiImageEditModel;
import io.github.nicolasbrum.provider.openai.OpenAiImageEditClient;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import java.time.Duration;
String apiKey = System.getenv("OPENAI_API_KEY");
var client = new OpenAiImageEditClient(
apiKey,
Duration.ofSeconds(30),
Duration.ofMinutes(3)
);
AiImageEditModel model = new DefaultAiImageEditModel(client);
var prompt = AiImageEditPrompt.builder()
.resource(new FileSystemResource("entrada.png"))
.mediaType(MediaType.IMAGE_PNG)
.instruction("Transforme a cena em uma ilustração em aquarela.")
.build();
var response = model.call(prompt);Nunca coloque a chave da API diretamente no código-fonte ou em arquivos versionados.
| Campo | Obrigatório | Comportamento |
|---|---|---|
resource |
Sim | Imagem de entrada representada por um Resource do Spring. |
mediaType |
Sim | Tipo MIME correspondente à imagem enviada. |
instruction / instructions |
Sim | Uma ou mais instruções. Elas são unidas com quebra de linha antes do envio. |
imageOptions |
Não | Opções do Spring AI. Quando omitidas, a biblioteca cria opções padrão. |
O builder rejeita prompts sem imagem, tipo de mídia ou instruções.
Na implementação atual, o cliente OpenAI utiliza estas opções:
| Opção | Default | Descrição |
|---|---|---|
model |
gpt-image-1 |
Modelo usado para editar a imagem. |
n |
1 |
Quantidade de imagens solicitadas. |
Embora ImageOptions também possua largura, altura, formato de resposta e estilo, esses valores ainda não são enviados pelo OpenAiImageEditClient. Defini-los nesta versão não altera a requisição. Essa distinção evita que uma configuração aparentemente aceita e produza expectativas incorretas.
| Propriedade | Default | Descrição |
|---|---|---|
spring.ai.openai.api-key |
— | Chave da OpenAI; tem prioridade sobre a propriedade alternativa. |
ai.image.edit.client.openai.api-key |
— | Propriedade alternativa para a chave da OpenAI. |
ai.image.edit.client.openai.connection-timeout |
PT30S |
Tempo máximo para estabelecer a conexão. |
ai.image.edit.client.openai.read-timeout |
PT3M |
Tempo máximo de espera pela resposta. |
Observação: Dependendo do tamanho da imagem, será necessário alterar a configuração de tamanho suportado à arquivos no framework.
O retorno é um ImageResponse. Cada item de getResults() contém uma imagem com o conteúdo Base64 em getB64Json():
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
var generation = response.getResult();
if (generation != null && generation.getOutput().getB64Json() != null) {
byte[] imageBytes = Base64.getDecoder()
.decode(generation.getOutput().getB64Json());
Files.write(Path.of("imagem-editada.png"), imageBytes);
}Os metadados da resposta podem incluir:
- instante de criação informado pelo provedor;
quality;outputFormat;size.
Esses campos dependem do conteúdo retornado pela OpenAI e podem não estar presentes em todas as respostas.
Nesta versão, a biblioteca:
- suporta o provedor OpenAI;
- aceita uma imagem de entrada por chamada;
- devolve imagens codificadas em Base64;
- executa chamadas síncronas;
- envia
modelencomo opções da edição.
A arquitetura separa o contrato comum dos detalhes da OpenAI para permitir que novos provedores sejam adicionados sem alterar a forma como a aplicação chama AiImageEditModel. As próximas evoluções naturais incluem ampliar as opções de edição, melhorar o tratamento de erros e oferecer novos adaptadores de provedores.