mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-26 17:21:53 +08:00
对接openai image
This commit is contained in:
parent
a14aebd5ee
commit
5999b80471
@ -19,7 +19,6 @@ package cn.iocoder.yudao.framework.ai.image;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.model.ModelClient;
|
import cn.iocoder.yudao.framework.ai.model.ModelClient;
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface ImageClient extends ModelClient<ImagePrompt, ImageResponse> {
|
public interface ImageClient extends ModelClient<ImagePrompt, ImageResponse> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,14 +1,25 @@
|
|||||||
package cn.iocoder.yudao.framework.ai.imageopenai;
|
package cn.iocoder.yudao.framework.ai.imageopenai;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageRequest;
|
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageRequest;
|
||||||
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageResponse;
|
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageResponse;
|
||||||
import cn.iocoder.yudao.framework.ai.util.JacksonUtil;
|
import cn.iocoder.yudao.framework.ai.util.JacksonUtil;
|
||||||
import io.netty.channel.ChannelOption;
|
import io.netty.channel.ChannelOption;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||||
import org.springframework.web.reactive.function.BodyInserters;
|
import org.springframework.web.reactive.function.BodyInserters;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
import reactor.netty.http.client.HttpClient;
|
import reactor.netty.http.client.HttpClient;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,6 +28,7 @@ import java.time.Duration;
|
|||||||
* author: fansili
|
* author: fansili
|
||||||
* time: 2024/3/17 09:53
|
* time: 2024/3/17 09:53
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class OpenAiImageApi {
|
public class OpenAiImageApi {
|
||||||
|
|
||||||
private static final String DEFAULT_BASE_URL = "https://api.openai.com";
|
private static final String DEFAULT_BASE_URL = "https://api.openai.com";
|
||||||
@ -24,6 +36,8 @@ public class OpenAiImageApi {
|
|||||||
// 发送请求 webClient
|
// 发送请求 webClient
|
||||||
private final WebClient webClient;
|
private final WebClient webClient;
|
||||||
|
|
||||||
|
private CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||||
|
|
||||||
public OpenAiImageApi(String apiKey) {
|
public OpenAiImageApi(String apiKey) {
|
||||||
this.apiKey = apiKey;
|
this.apiKey = apiKey;
|
||||||
// 创建一个HttpClient实例并设置超时
|
// 创建一个HttpClient实例并设置超时
|
||||||
@ -37,18 +51,40 @@ public class OpenAiImageApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public OpenAiImageResponse createImage(OpenAiImageRequest request) {
|
public OpenAiImageResponse createImage(OpenAiImageRequest request) {
|
||||||
String res = webClient.post()
|
HttpPost httpPost = new HttpPost();
|
||||||
.uri(uriBuilder -> uriBuilder.path("/v1/images/generations").build())
|
httpPost.setURI(URI.create(DEFAULT_BASE_URL.concat("/v1/images/generations")));
|
||||||
.header("Content-Type", "application/json")
|
httpPost.setHeader("Content-Type", "application/json");
|
||||||
.header("Authorization", "Bearer " + apiKey)
|
httpPost.setHeader("Authorization", "Bearer " + apiKey);
|
||||||
// 设置请求体(这里假设jsonStr是一个JSON格式的字符串)
|
httpPost.setEntity(new StringEntity(JacksonUtil.toJson(request), "UTF-8"));
|
||||||
.body(BodyInserters.fromValue(JacksonUtil.toJson(request)))
|
|
||||||
// 发送请求并获取响应体
|
CloseableHttpResponse response= null;
|
||||||
.retrieve()
|
try {
|
||||||
// 转换响应体为String类型
|
response = httpclient.execute(httpPost);
|
||||||
.bodyToMono(String.class)
|
HttpEntity entity = response.getEntity();
|
||||||
.block();
|
String resultJson = EntityUtils.toString(entity);
|
||||||
// TODO: 2024/3/17 这里发送请求会失败!
|
log.info("openai 图片生成结果: {}", resultJson);
|
||||||
return null;
|
return JSONUtil.toBean(resultJson, OpenAiImageResponse.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
if (response != null) {
|
||||||
|
try {
|
||||||
|
response.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// String res = webClient.post()
|
||||||
|
// .uri(uriBuilder -> uriBuilder.path("/v1/images/generations").build())
|
||||||
|
// .header("Content-Type", "application/json")
|
||||||
|
// .header("Authorization", "Bearer " + apiKey)
|
||||||
|
// // 设置请求体(这里假设jsonStr是一个JSON格式的字符串)
|
||||||
|
// .body(BodyInserters.fromValue(JacksonUtil.toJson(request)))
|
||||||
|
// // 发送请求并获取响应体
|
||||||
|
// .retrieve()
|
||||||
|
// // 转换响应体为String类型
|
||||||
|
// .bodyToMono(String.class)
|
||||||
|
// .block();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package cn.iocoder.yudao.framework.ai.imageopenai;
|
package cn.iocoder.yudao.framework.ai.imageopenai;
|
||||||
|
|
||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
import cn.iocoder.yudao.framework.ai.chat.ChatException;
|
import cn.iocoder.yudao.framework.ai.chat.ChatException;
|
||||||
import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException;
|
import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException;
|
||||||
import cn.iocoder.yudao.framework.ai.image.ImageClient;
|
import cn.iocoder.yudao.framework.ai.image.*;
|
||||||
import cn.iocoder.yudao.framework.ai.image.ImageOptions;
|
|
||||||
import cn.iocoder.yudao.framework.ai.image.ImagePrompt;
|
|
||||||
import cn.iocoder.yudao.framework.ai.image.ImageResponse;
|
|
||||||
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageRequest;
|
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageRequest;
|
||||||
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageResponse;
|
import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageResponse;
|
||||||
import jdk.jfr.Frequency;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.retry.RetryCallback;
|
import org.springframework.retry.RetryCallback;
|
||||||
import org.springframework.retry.RetryContext;
|
import org.springframework.retry.RetryContext;
|
||||||
@ -74,9 +72,15 @@ public class OpenAiImageClient implements ImageClient {
|
|||||||
// 创建请求
|
// 创建请求
|
||||||
OpenAiImageRequest request = new OpenAiImageRequest();
|
OpenAiImageRequest request = new OpenAiImageRequest();
|
||||||
BeanUtil.copyProperties(openAiImageOptions, request);
|
BeanUtil.copyProperties(openAiImageOptions, request);
|
||||||
|
request.setPrompt(imagePrompt.getInstructions().get(0).getText());
|
||||||
// 发送请求
|
// 发送请求
|
||||||
OpenAiImageResponse response = openAiImageApi.createImage(request);
|
OpenAiImageResponse response = openAiImageApi.createImage(request);
|
||||||
return null;
|
return new ImageResponse(response.getData().stream().map(res -> {
|
||||||
|
byte[] bytes = HttpUtil.downloadBytes(res.getUrl());
|
||||||
|
String base64 = Base64.encode(bytes);
|
||||||
|
return new ImageGeneration(new Image(res.getUrl(), base64));
|
||||||
|
}).toList());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.ai.imageopenai;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.image.ImageOptions;
|
import cn.iocoder.yudao.framework.ai.image.ImageOptions;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,6 +48,21 @@ public class OpenAiImageOptions implements ImageOptions {
|
|||||||
// 代表您的终端用户的唯一标识符,有助于OpenAI监控并检测滥用行为。了解更多信息请参考官方文档。
|
// 代表您的终端用户的唯一标识符,有助于OpenAI监控并检测滥用行为。了解更多信息请参考官方文档。
|
||||||
private String endUserId;
|
private String endUserId;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum ResponseFormatEnum {
|
||||||
|
|
||||||
|
URL("url"),
|
||||||
|
BASE64("b64_json"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
ResponseFormatEnum(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// 适配 spring ai
|
// 适配 spring ai
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ public class OpenAiImageResponse {
|
|||||||
public static class Item {
|
public static class Item {
|
||||||
|
|
||||||
private String url;
|
private String url;
|
||||||
|
private String b64_json;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,14 @@ import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageOptions;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* author: fansili
|
* author: fansili
|
||||||
* time: 2024/3/17 10:40
|
* time: 2024/3/17 10:40
|
||||||
@ -20,12 +28,40 @@ public class OpenAiImageClientTests {
|
|||||||
// 初始化 openAiImageClient
|
// 初始化 openAiImageClient
|
||||||
this.openAiImageClient = new OpenAiImageClient(
|
this.openAiImageClient = new OpenAiImageClient(
|
||||||
new OpenAiImageApi(""),
|
new OpenAiImageApi(""),
|
||||||
new OpenAiImageOptions()
|
new OpenAiImageOptions().setResponseFormat(OpenAiImageOptions.ResponseFormatEnum.URL.getValue())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void callTest() {
|
public void callTest() {
|
||||||
openAiImageClient.call(new ImagePrompt("我和我的小狗,一起在北极和企鹅玩排球。"));
|
ImageResponse call = openAiImageClient.call(new ImagePrompt("我和我的小狗,一起在北极和企鹅玩排球。"));
|
||||||
|
System.err.println("url: " + call.getResult().getOutput().getUrl());
|
||||||
|
System.err.println("base64: " + call.getResult().getOutput().getB64Json());
|
||||||
|
|
||||||
|
String base64String = call.getResult().getOutput().getB64Json();
|
||||||
|
ImageIcon imageIcon = new ImageIcon(decodeBase64ToImage(base64String));
|
||||||
|
JLabel label = new JLabel(imageIcon);
|
||||||
|
|
||||||
|
JFrame frame = new JFrame("Base64 Image Display");
|
||||||
|
frame.getContentPane().add(label);
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
// 阻止退出
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
scanner.nextLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 将Base64解码为BufferedImage
|
||||||
|
private static BufferedImage decodeBase64ToImage(String base64String) {
|
||||||
|
try {
|
||||||
|
byte[] decodedBytes = Base64.getDecoder().decode(base64String);
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(decodedBytes);
|
||||||
|
return ImageIO.read(bis);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Error decoding the base64 image: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user