Commit 73502d565fad087689a851b9eb1edb35eb3d50cc

Authored by YunaiV
1 parent 4311fe45

【代码优化】AI:ChatGlm 替换成 ZhiPuAiImage 实现

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
... ... @@ -9,7 +9,6 @@ import cn.hutool.core.util.StrUtil;
9 9 import cn.hutool.extra.spring.SpringUtil;
10 10 import cn.hutool.http.HttpUtil;
11 11 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
12   -import cn.iocoder.yudao.framework.ai.core.model.chatglm.ChatGlmImageOptions;
13 12 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
14 13 import cn.iocoder.yudao.framework.common.pojo.PageParam;
15 14 import cn.iocoder.yudao.framework.common.pojo.PageResult;
... ... @@ -34,6 +33,7 @@ import org.springframework.ai.image.ImageResponse;
34 33 import org.springframework.ai.openai.OpenAiImageOptions;
35 34 import org.springframework.ai.qianfan.QianFanImageOptions;
36 35 import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
  36 +import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
37 37 import org.springframework.scheduling.annotation.Async;
38 38 import org.springframework.stereotype.Service;
39 39 import org.springframework.transaction.annotation.Transactional;
... ... @@ -105,7 +105,9 @@ public class AiImageServiceImpl implements AiImageService {
105 105 ImageResponse response = imageModel.call(new ImagePrompt(req.getPrompt(), request));
106 106  
107 107 // 2. 上传到文件服务
108   - byte[] fileContent = Base64.decode(response.getResult().getOutput().getB64Json());
  108 + String b64Json = response.getResult().getOutput().getB64Json();
  109 + byte[] fileContent = StrUtil.isNotEmpty(b64Json) ? Base64.decode(b64Json)
  110 + : HttpUtil.downloadBytes(response.getResult().getOutput().getUrl());
109 111 String filePath = fileApi.createFile(fileContent);
110 112  
111 113 // 3. 更新数据库
... ... @@ -149,8 +151,8 @@ public class AiImageServiceImpl implements AiImageService {
149 151 .withModel(draw.getModel()).withN(1)
150 152 .withHeight(draw.getHeight()).withWidth(draw.getWidth())
151 153 .build();
152   - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.CHATGLM.getPlatform())) {
153   - return ChatGlmImageOptions.builder()
  154 + } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
  155 + return ZhiPuAiImageOptions.builder()
154 156 .withModel(draw.getModel())
155 157 .build();
156 158 }
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
... ... @@ -60,13 +60,6 @@
60 60 <version>2.14.0</version>
61 61 </dependency>
62 62  
63   - <!-- bigmodel -->
64   - <dependency>
65   - <groupId>cn.bigmodel.openapi</groupId>
66   - <artifactId>oapi-java-sdk</artifactId>
67   - <version>release-V4-2.0.2</version>
68   - </dependency>
69   -
70 63 <!-- Test 测试相关 -->
71 64 <dependency>
72 65 <groupId>org.springframework.boot</groupId>
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java
... ... @@ -28,7 +28,6 @@ public enum AiPlatformEnum {
28 28 STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI
29 29 MIDJOURNEY("Midjourney", "Midjourney"), // Midjourney
30 30 SUNO("Suno", "Suno"), // Suno AI
31   - CHATGLM("ChatGlm", "ChatGlm"), // Suno AI
32 31  
33 32 ;
34 33  
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
... ... @@ -9,7 +9,6 @@ import cn.hutool.extra.spring.SpringUtil;
9 9 import cn.iocoder.yudao.framework.ai.config.YudaoAiAutoConfiguration;
10 10 import cn.iocoder.yudao.framework.ai.config.YudaoAiProperties;
11 11 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
12   -import cn.iocoder.yudao.framework.ai.core.model.chatglm.ChatGlmImageModel;
13 12 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
14 13 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
15 14 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
... ... @@ -31,6 +30,7 @@ import org.springframework.ai.autoconfigure.qianfan.QianFanImageProperties;
31 30 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration;
32 31 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiChatProperties;
33 32 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties;
  33 +import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiImageProperties;
34 34 import org.springframework.ai.chat.model.ChatModel;
35 35 import org.springframework.ai.image.ImageModel;
36 36 import org.springframework.ai.model.function.FunctionCallbackContext;
... ... @@ -48,7 +48,9 @@ import org.springframework.ai.qianfan.api.QianFanImageApi;
48 48 import org.springframework.ai.stabilityai.StabilityAiImageModel;
49 49 import org.springframework.ai.stabilityai.api.StabilityAiApi;
50 50 import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
  51 +import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
51 52 import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
  53 +import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
52 54 import org.springframework.retry.support.RetryTemplate;
53 55 import org.springframework.web.client.ResponseErrorHandler;
54 56 import org.springframework.web.client.RestClient;
... ... @@ -119,6 +121,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
119 121 return SpringUtil.getBean(TongYiImagesModel.class);
120 122 case YI_YAN:
121 123 return SpringUtil.getBean(QianFanImageModel.class);
  124 + case ZHI_PU:
  125 + return SpringUtil.getBean(ZhiPuAiImageModel.class);
122 126 case OPENAI:
123 127 return SpringUtil.getBean(OpenAiImageModel.class);
124 128 case STABLE_DIFFUSION:
... ... @@ -136,12 +140,12 @@ public class AiModelFactoryImpl implements AiModelFactory {
136 140 return buildTongYiImagesModel(apiKey);
137 141 case YI_YAN:
138 142 return buildQianFanImageModel(apiKey);
  143 + case ZHI_PU:
  144 + return buildZhiPuAiImageModel(apiKey, url);
139 145 case OPENAI:
140 146 return buildOpenAiImageModel(apiKey, url);
141 147 case STABLE_DIFFUSION:
142 148 return buildStabilityAiImageModel(apiKey, url);
143   - case CHATGLM:
144   - return buildChatGlmModel(apiKey);
145 149 default:
146 150 throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
147 151 }
... ... @@ -225,7 +229,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
225 229 }
226 230  
227 231 /**
228   - * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
  232 + * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(
  233 + * ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
229 234 */
230 235 private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
231 236 url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
... ... @@ -234,6 +239,16 @@ public class AiModelFactoryImpl implements AiModelFactory {
234 239 }
235 240  
236 241 /**
  242 + * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel(
  243 + * ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
  244 + */
  245 + private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {
  246 + url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
  247 + ZhiPuAiImageApi zhiPuAiApi = new ZhiPuAiImageApi(url, apiKey, RestClient.builder());
  248 + return new ZhiPuAiImageModel(zhiPuAiApi);
  249 + }
  250 +
  251 + /**
237 252 * 可参考 {@link YudaoAiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)}
238 253 */
239 254 private static XingHuoChatModel buildXingHuoChatModel(String key) {
... ... @@ -276,7 +291,4 @@ public class AiModelFactoryImpl implements AiModelFactory {
276 291 return new StabilityAiImageModel(stabilityAiApi);
277 292 }
278 293  
279   - private ChatGlmImageModel buildChatGlmModel(String apiKey) {
280   - return new ChatGlmImageModel(apiKey);
281   - }
282 294 }
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/chatglm/ChatGlmImageModel.java deleted
1   -package cn.iocoder.yudao.framework.ai.core.model.chatglm;
2   -
3   -import cn.iocoder.yudao.framework.ai.core.model.chatglm.api.ChatGlmResponseMetadata;
4   -import com.zhipu.oapi.ClientV4;
5   -import com.zhipu.oapi.service.v4.image.CreateImageRequest;
6   -import com.zhipu.oapi.service.v4.image.ImageApiResponse;
7   -import org.springframework.ai.image.*;
8   -
9   -import java.io.ByteArrayOutputStream;
10   -import java.net.URL;
11   -import java.util.Base64;
12   -import java.util.stream.Collectors;
13   -
14   -public class ChatGlmImageModel implements ImageModel {
15   -
16   - private ClientV4 client;
17   -
18   - public ChatGlmImageModel(String apiSecretKey) {
19   - client = new ClientV4.Builder(apiSecretKey).build();
20   - }
21   -
22   - @Override
23   - public ImageResponse call(ImagePrompt request) {
24   - CreateImageRequest imageRequest = CreateImageRequest.builder()
25   - .model(request.getOptions().getModel())
26   - .prompt(request.getInstructions().get(0).getText())
27   - .build();
28   - return convert(client.createImage(imageRequest));
29   - }
30   -
31   - private ImageResponse convert(ImageApiResponse result) {
32   - return new ImageResponse(
33   - result.getData().getData().stream().map(item -> {
34   - try {
35   - String url = item.getUrl();
36   - String base64Image = convertImageToBase64(url);
37   - Image image = new Image(url, base64Image);
38   - return new ImageGeneration(image);
39   - } catch (Exception e) {
40   - throw new RuntimeException(e);
41   - }
42   - }).collect(Collectors.toList()),
43   - new ChatGlmResponseMetadata(result)
44   - );
45   - }
46   -
47   -
48   - /**
49   - * Convert image to base64.
50   - * @param imageUrl the image url.
51   - * @return the base64 image.
52   - * @throws Exception the exception.
53   - */
54   - public String convertImageToBase64(String imageUrl) throws Exception {
55   -
56   - var url = new URL(imageUrl);
57   - var inputStream = url.openStream();
58   - var outputStream = new ByteArrayOutputStream();
59   - var buffer = new byte[4096];
60   - int bytesRead;
61   -
62   - while ((bytesRead = inputStream.read(buffer)) != -1) {
63   - outputStream.write(buffer, 0, bytesRead);
64   - }
65   -
66   - var imageBytes = outputStream.toByteArray();
67   -
68   - String base64Image = Base64.getEncoder().encodeToString(imageBytes);
69   -
70   - inputStream.close();
71   - outputStream.close();
72   -
73   - return base64Image;
74   - }
75   -}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/chatglm/ChatGlmImageOptions.java deleted
1   -package cn.iocoder.yudao.framework.ai.core.model.chatglm;
2   -
3   -import com.fasterxml.jackson.annotation.JsonProperty;
4   -import lombok.Setter;
5   -import org.springframework.ai.image.ImageOptions;
6   -
7   -/**
8   - * chatglm
9   - * api地址:https://open.bigmodel.cn/dev/api#cogview
10   - */
11   -@Setter
12   -public class ChatGlmImageOptions implements ImageOptions {
13   -
14   - @JsonProperty("n")
15   - private Integer n;
16   -
17   - @JsonProperty("model")
18   - private String model = "cogview-3";
19   -
20   - @JsonProperty("size_width")
21   - private Integer width;
22   -
23   - @JsonProperty("size_height")
24   - private Integer height;
25   -
26   - @JsonProperty("size")
27   - private String size;
28   -
29   - @JsonProperty("style")
30   - private String style;
31   -
32   - @JsonProperty("user_id")
33   - private String user;
34   -
35   - @JsonProperty("responseFormat")
36   - private String responseFormat;
37   -
38   - // ==== build
39   -
40   -
41   - public static ChatGlmImageOptions.Builder builder() {
42   - return new ChatGlmImageOptions.Builder();
43   - }
44   -
45   - public static class Builder {
46   -
47   - private final ChatGlmImageOptions options;
48   -
49   - private Builder() {
50   - this.options = new ChatGlmImageOptions();
51   - }
52   -
53   - public ChatGlmImageOptions.Builder withN(Integer n) {
54   - options.setN(n);
55   - return this;
56   - }
57   -
58   - public ChatGlmImageOptions.Builder withModel(String model) {
59   - options.setModel(model);
60   - return this;
61   - }
62   -
63   - public ChatGlmImageOptions.Builder withWidth(Integer width) {
64   - options.setWidth(width);
65   - return this;
66   - }
67   -
68   - public ChatGlmImageOptions.Builder withHeight(Integer height) {
69   - options.setHeight(height);
70   - return this;
71   - }
72   -
73   - public ChatGlmImageOptions.Builder withStyle(String style) {
74   - options.setStyle(style);
75   - return this;
76   - }
77   -
78   - public ChatGlmImageOptions.Builder withUser(String user) {
79   - options.setUser(user);
80   - return this;
81   - }
82   -
83   - public ChatGlmImageOptions build() {
84   - return options;
85   - }
86   -
87   - }
88   -
89   - // ==== get
90   -
91   - @Override
92   - public Integer getN() {
93   - return n;
94   - }
95   -
96   - @Override
97   - public String getModel() {
98   - return model;
99   - }
100   -
101   - @Override
102   - public Integer getWidth() {
103   - return width;
104   - }
105   -
106   - @Override
107   - public Integer getHeight() {
108   - return height;
109   - }
110   -
111   - @Override
112   - public String getResponseFormat() {
113   - return responseFormat;
114   - }
115   -}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/chatglm/api/ChatGlmResponseMetadata.java deleted
1   -package cn.iocoder.yudao.framework.ai.core.model.chatglm.api;
2   -
3   -import com.zhipu.oapi.service.v4.image.ImageApiResponse;
4   -import org.springframework.ai.image.ImageResponseMetadata;
5   -
6   -import java.util.HashMap;
7   -
8   -public class ChatGlmResponseMetadata extends HashMap<String, Object> implements ImageResponseMetadata {
9   -
10   - private Long created;
11   -
12   - public ChatGlmResponseMetadata(ImageApiResponse result) {
13   - created = result.getData().getCreated();
14   - }
15   -
16   - @Override
17   - public Long getCreated() {
18   - return created;
19   - }
20   -
21   - public void setCreated(Long created) {
22   - this.created = created;
23   - }
24   -}
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/ChatGlmImageModelTests.java deleted
1   -package cn.iocoder.yudao.framework.ai.image;
2   -
3   -import cn.iocoder.yudao.framework.ai.core.model.chatglm.ChatGlmImageModel;
4   -import cn.iocoder.yudao.framework.ai.core.model.chatglm.ChatGlmImageOptions;
5   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6   -import com.alibaba.fastjson.JSON;
7   -import com.zhipu.oapi.ClientV4;
8   -import com.zhipu.oapi.core.httpclient.ApacheHttpClientTransport;
9   -import com.zhipu.oapi.service.v4.image.CreateImageRequest;
10   -import com.zhipu.oapi.service.v4.image.ImageApiResponse;
11   -import org.junit.jupiter.api.Test;
12   -import org.springframework.ai.image.ImageOptionsBuilder;
13   -import org.springframework.ai.image.ImagePrompt;
14   -import org.springframework.ai.image.ImageResponse;
15   -import org.springframework.ai.qianfan.QianFanImageModel;
16   -import org.springframework.ai.qianfan.QianFanImageOptions;
17   -import org.springframework.ai.qianfan.api.QianFanImageApi;
18   -
19   -/**
20   - * 百度千帆 image
21   - */
22   -public class ChatGlmImageModelTests {
23   -
24   - @Test
25   - public void callTest() {
26   - ChatGlmImageModel model = new ChatGlmImageModel("78d3228c1d9e5e342a3e1ab349e2dd7b.VXLoq5vrwK2ofboy");
27   - ImageResponse call = model.call(new ImagePrompt("万里长城", ChatGlmImageOptions.builder().build()));
28   - System.err.println(call.getResult().getOutput().getUrl());
29   - }
30   -
31   - @Test
32   - public void createImageTest() {
33   - ClientV4 client = new ClientV4.Builder("78d3228c1d9e5e342a3e1ab349e2dd7b.VXLoq5vrwK2ofboy").build();
34   - CreateImageRequest createImageRequest = new CreateImageRequest();
35   - createImageRequest.setModel("cogview-3");
36   - createImageRequest.setPrompt("长城!");
37   - ImageApiResponse image = client.createImage(createImageRequest);
38   - System.err.println(JSON.toJSONString(image));
39   - }
40   -}
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/ZhiPuAiImageModelTests.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.image;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.ai.image.ImagePrompt;
  6 +import org.springframework.ai.image.ImageResponse;
  7 +import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
  8 +import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
  9 +import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
  10 +
  11 +/**
  12 + * {@link ZhiPuAiImageModel} 集成测试
  13 + */
  14 +public class ZhiPuAiImageModelTests {
  15 +
  16 + private final ZhiPuAiImageApi imageApi = new ZhiPuAiImageApi(
  17 + "78d3228c1d9e5e342a3e1ab349e2dd7b.VXLoq5vrwK2ofboy");
  18 + private final ZhiPuAiImageModel imageModel = new ZhiPuAiImageModel(imageApi);
  19 +
  20 + @Test
  21 + @Disabled
  22 + public void testCall() {
  23 + // 准备参数
  24 + ZhiPuAiImageOptions imageOptions = ZhiPuAiImageOptions.builder()
  25 + .withModel(ZhiPuAiImageApi.ImageModel.CogView_3.getValue())
  26 + .build();
  27 + ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions);
  28 +
  29 + // 方法调用
  30 + ImageResponse response = imageModel.call(prompt);
  31 + // 打印结果
  32 + System.out.println(response);
  33 + }
  34 +
  35 +}
... ...