Commit 2ae90b9edc327db77e77d9aaa7e15cfbf62853b1
Merge remote-tracking branch 'origin/master-jdk21-ai' into master-jdk21-ai
Showing
15 changed files
with
209 additions
and
145 deletions
script/idea/http-client.env.json
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
yudao-module-ai/yudao-module-ai-biz/pom.xml
| ... | ... | @@ -60,9 +60,5 @@ |
| 60 | 60 | <groupId>cn.iocoder.boot</groupId> |
| 61 | 61 | <artifactId>yudao-spring-boot-starter-test</artifactId> |
| 62 | 62 | </dependency> |
| 63 | - <dependency> | |
| 64 | - <groupId>cn.iocoder.boot</groupId> | |
| 65 | - <artifactId>yudao-spring-boot-starter-excel</artifactId> | |
| 66 | - </dependency> | |
| 67 | 63 | </dependencies> |
| 68 | 64 | </project> |
| 69 | 65 | \ No newline at end of file | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
| ... | ... | @@ -12,8 +12,7 @@ import lombok.Data; |
| 12 | 12 | * |
| 13 | 13 | * @author xiaoxin |
| 14 | 14 | */ |
| 15 | -// TODO @xin:如果没 typehandler 的需求,autoResultMap 可以去掉哈 | |
| 16 | -@TableName(value = "ai_mind_map", autoResultMap = true) | |
| 15 | +@TableName(value = "ai_mind_map") | |
| 17 | 16 | @Data |
| 18 | 17 | public class AiMindMapDO extends BaseDO { |
| 19 | 18 | |
| ... | ... | @@ -25,7 +24,7 @@ public class AiMindMapDO extends BaseDO { |
| 25 | 24 | |
| 26 | 25 | /** |
| 27 | 26 | * 用户编号 |
| 28 | - * | |
| 27 | + * <p> | |
| 29 | 28 | * 关联 AdminUserDO 的 userId 字段 |
| 30 | 29 | */ |
| 31 | 30 | private Long userId; | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/mindmap/AiMindMapMapper.java
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| ... | ... | @@ -111,7 +111,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 111 | 111 | AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, |
| 112 | 112 | userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext()); |
| 113 | 113 | |
| 114 | - // 3.2 创建 chat 需要的 Prompt | |
| 114 | + // 3.2 构建 Prompt,并进行调用 | |
| 115 | 115 | Prompt prompt = buildPrompt(conversation, historyMessages, model, sendReqVO); |
| 116 | 116 | Flux<ChatResponse> streamResponse = chatModel.stream(prompt); |
| 117 | 117 | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
| ... | ... | @@ -31,6 +31,7 @@ import org.springframework.ai.image.ImageOptions; |
| 31 | 31 | import org.springframework.ai.image.ImagePrompt; |
| 32 | 32 | import org.springframework.ai.image.ImageResponse; |
| 33 | 33 | import org.springframework.ai.openai.OpenAiImageOptions; |
| 34 | +import org.springframework.ai.qianfan.QianFanImageOptions; | |
| 34 | 35 | import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; |
| 35 | 36 | import org.springframework.scheduling.annotation.Async; |
| 36 | 37 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -142,6 +143,11 @@ public class AiImageServiceImpl implements AiImageService { |
| 142 | 143 | .withModel(draw.getModel()).withN(1) |
| 143 | 144 | .withHeight(draw.getHeight()).withWidth(draw.getWidth()) |
| 144 | 145 | .build(); |
| 146 | + } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) { | |
| 147 | + return QianFanImageOptions.builder() | |
| 148 | + .withModel(draw.getModel()).withN(1) | |
| 149 | + .withHeight(draw.getHeight()).withWidth(draw.getWidth()) | |
| 150 | + .build(); | |
| 145 | 151 | } |
| 146 | 152 | throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform()); |
| 147 | 153 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.service.mindmap; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 6 | 7 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; |
| ... | ... | @@ -31,13 +32,12 @@ import reactor.core.publisher.Flux; |
| 31 | 32 | |
| 32 | 33 | import java.util.ArrayList; |
| 33 | 34 | import java.util.List; |
| 34 | -import java.util.Objects; | |
| 35 | 35 | |
| 36 | 36 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; |
| 37 | 37 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 38 | 38 | |
| 39 | 39 | /** |
| 40 | - * AI 写作 Service 实现类 | |
| 40 | + * AI 思维导图 Service 实现类 | |
| 41 | 41 | * |
| 42 | 42 | * @author xiaoxin |
| 43 | 43 | */ |
| ... | ... | @@ -57,38 +57,28 @@ public class AiMindMapServiceImpl implements AiMindMapService { |
| 57 | 57 | |
| 58 | 58 | @Override |
| 59 | 59 | public Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId) { |
| 60 | - // 1.1 获取脑图模型 尝试获取思维导图助手角色,如果没有则使用默认模型 | |
| 61 | - AiChatRoleDO mindMapRole = CollUtil.getFirst(chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName())); | |
| 62 | - AiChatModelDO model; | |
| 63 | - String systemMessage; | |
| 64 | - if (Objects.nonNull(mindMapRole) && Objects.nonNull(mindMapRole.getModelId())) { | |
| 65 | - model = chatModalService.getChatModel(mindMapRole.getModelId()); | |
| 66 | - systemMessage = mindMapRole.getSystemMessage(); | |
| 67 | - } else { | |
| 68 | - model = chatModalService.getRequiredDefaultChatModel(); | |
| 69 | - systemMessage = AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage(); | |
| 70 | - } | |
| 71 | - | |
| 60 | + // 1. 获取脑图模型。尝试获取思维导图助手角色,如果没有则使用默认模型 | |
| 61 | + AiChatRoleDO role = CollUtil.getFirst( | |
| 62 | + chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName())); | |
| 63 | + // 1.1 获取脑图执行模型 | |
| 64 | + AiChatModelDO model = getModel(role); | |
| 65 | + // 1.2 获取角色设定消息 | |
| 66 | + String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage()) | |
| 67 | + ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage(); | |
| 68 | + // 1.3 校验平台 | |
| 72 | 69 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 73 | 70 | ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); |
| 74 | 71 | |
| 75 | - // 2 插入思维导图信息 | |
| 76 | - AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, e -> e.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform())); | |
| 72 | + // 2. 插入思维导图信息 | |
| 73 | + AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, | |
| 74 | + mindMap -> mindMap.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform())); | |
| 77 | 75 | mindMapMapper.insert(mindMapDO); |
| 78 | 76 | |
| 79 | - ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens()); | |
| 80 | - // 3.1 角色设定 | |
| 81 | - List<Message> chatMessages = new ArrayList<>(); | |
| 82 | - if (StrUtil.isNotBlank(systemMessage)) { | |
| 83 | - chatMessages.add(new SystemMessage(systemMessage)); | |
| 84 | - } | |
| 85 | - // 3.2 用户输入 | |
| 86 | - chatMessages.add(new UserMessage(generateReqVO.getPrompt())); | |
| 87 | - // 3.3 构建提示词 | |
| 88 | - Prompt prompt = new Prompt(chatMessages, chatOptions); | |
| 89 | - | |
| 77 | + // 3.1 构建 Prompt,并进行调用 | |
| 78 | + Prompt prompt = buildPrompt(generateReqVO, model, systemMessage); | |
| 90 | 79 | Flux<ChatResponse> streamResponse = chatModel.stream(prompt); |
| 91 | - // 3.4 流式返回 | |
| 80 | + | |
| 81 | + // 3.2 流式返回 | |
| 92 | 82 | StringBuffer contentBuffer = new StringBuffer(); |
| 93 | 83 | return streamResponse.map(chunk -> { |
| 94 | 84 | String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; |
| ... | ... | @@ -109,4 +99,36 @@ public class AiMindMapServiceImpl implements AiMindMapService { |
| 109 | 99 | |
| 110 | 100 | } |
| 111 | 101 | |
| 102 | + private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) { | |
| 103 | + // 1. 构建 message 列表 | |
| 104 | + List<Message> chatMessages = buildMessages(generateReqVO, systemMessage); | |
| 105 | + // 2. 构建 options 对象 | |
| 106 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); | |
| 107 | + ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens()); | |
| 108 | + return new Prompt(chatMessages, options); | |
| 109 | + } | |
| 110 | + | |
| 111 | + private static List<Message> buildMessages(AiMindMapGenerateReqVO generateReqVO, String systemMessage) { | |
| 112 | + List<Message> chatMessages = new ArrayList<>(); | |
| 113 | + // 1. 角色设定 | |
| 114 | + if (StrUtil.isNotBlank(systemMessage)) { | |
| 115 | + chatMessages.add(new SystemMessage(systemMessage)); | |
| 116 | + } | |
| 117 | + // 2. 用户输入 | |
| 118 | + chatMessages.add(new UserMessage(generateReqVO.getPrompt())); | |
| 119 | + return chatMessages; | |
| 120 | + } | |
| 121 | + | |
| 122 | + private AiChatModelDO getModel(AiChatRoleDO role) { | |
| 123 | + AiChatModelDO model = null; | |
| 124 | + if (role != null && role.getModelId() != null) { | |
| 125 | + model = chatModalService.getChatModel(role.getModelId()); | |
| 126 | + } | |
| 127 | + if (model != null) { | |
| 128 | + model = chatModalService.getRequiredDefaultChatModel(); | |
| 129 | + } | |
| 130 | + Assert.notNull(model, "[AI] 获取不到模型"); | |
| 131 | + return model; | |
| 132 | + } | |
| 133 | + | |
| 112 | 134 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.service.write; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 6 | 7 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; |
| ... | ... | @@ -67,19 +68,15 @@ public class AiWriteServiceImpl implements AiWriteService { |
| 67 | 68 | |
| 68 | 69 | @Override |
| 69 | 70 | public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) { |
| 70 | - // 1.1 获取写作模型 尝试获取写作助手角色,如果没有则使用默认模型 | |
| 71 | - AiChatRoleDO writeRole = CollUtil.getFirst(chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName())); | |
| 72 | - // TODO @xin:如果有 writeRole,但是没 modeId,是不是也可以用 systemMessage 哈?建议的写法是:先通过 modelId 获取 model。如果 model == null,则 chatModalService.getRequiredDefaultChatModel();如果还是 null,则抛出异常;。。。。。。。。。。。。。。然后,systemMessage = writeRole != null && writeRole.systemPrompt != "" 这样处理。 | |
| 73 | - AiChatModelDO model; | |
| 74 | - String systemMessage; | |
| 75 | - if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) { | |
| 76 | - model = chatModalService.getChatModel(writeRole.getModelId()); | |
| 77 | - systemMessage = writeRole.getSystemMessage(); | |
| 78 | - } else { | |
| 79 | - model = chatModalService.getRequiredDefaultChatModel(); | |
| 80 | - systemMessage = AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage(); | |
| 81 | - } | |
| 82 | - // 1.2 校验平台 | |
| 71 | + // 1 获取写作模型。尝试获取写作助手角色,没有则使用默认模型 | |
| 72 | + AiChatRoleDO writeRole = CollUtil.getFirst( | |
| 73 | + chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName())); | |
| 74 | + // 1.1 获取写作执行模型 | |
| 75 | + AiChatModelDO model = getModel(writeRole); | |
| 76 | + // 1.2 获取角色设定消息 | |
| 77 | + String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage()) | |
| 78 | + ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage(); | |
| 79 | + // 1.3 校验平台 | |
| 83 | 80 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 84 | 81 | StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); |
| 85 | 82 | |
| ... | ... | @@ -88,21 +85,11 @@ public class AiWriteServiceImpl implements AiWriteService { |
| 88 | 85 | write -> write.setUserId(userId).setPlatform(platform.getPlatform()).setModel(model.getModel())); |
| 89 | 86 | writeMapper.insert(writeDO); |
| 90 | 87 | |
| 91 | - // 3. 调用大模型,写作生成 | |
| 92 | - ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens()); | |
| 93 | - // 3.1 角色设定 | |
| 94 | - // TODO @xin:要不把 90 到 97 这部分,合并到一个方法里。目的是:让这个方法的主干更明确 | |
| 95 | - List<Message> chatMessages = new ArrayList<>(); | |
| 96 | - if (StrUtil.isNotBlank(systemMessage)) { | |
| 97 | - chatMessages.add(new SystemMessage(systemMessage)); | |
| 98 | - } | |
| 99 | - // 3.2 用户输入 | |
| 100 | - chatMessages.add(new UserMessage(buildWritingPrompt(generateReqVO))); | |
| 101 | - // 3.3 构建提示词 | |
| 102 | - Prompt prompt = new Prompt(chatMessages, chatOptions); | |
| 88 | + // 3.1 构建 Prompt,并进行调用 | |
| 89 | + Prompt prompt = buildPrompt(generateReqVO, model, systemMessage); | |
| 103 | 90 | Flux<ChatResponse> streamResponse = chatModel.stream(prompt); |
| 104 | 91 | |
| 105 | - // 4. 流式返回 | |
| 92 | + // 3.2 流式返回 | |
| 106 | 93 | StringBuffer contentBuffer = new StringBuffer(); |
| 107 | 94 | return streamResponse.map(chunk -> { |
| 108 | 95 | String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; |
| ... | ... | @@ -122,7 +109,39 @@ public class AiWriteServiceImpl implements AiWriteService { |
| 122 | 109 | }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR))); |
| 123 | 110 | } |
| 124 | 111 | |
| 125 | - private String buildWritingPrompt(AiWriteGenerateReqVO generateReqVO) { | |
| 112 | + private AiChatModelDO getModel(AiChatRoleDO writeRole) { | |
| 113 | + AiChatModelDO model = null; | |
| 114 | + if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) { | |
| 115 | + model = chatModalService.getChatModel(writeRole.getModelId()); | |
| 116 | + } | |
| 117 | + if (Objects.isNull(model)) { | |
| 118 | + model = chatModalService.getRequiredDefaultChatModel(); | |
| 119 | + } | |
| 120 | + Assert.notNull(model, "[AI] 获取不到模型"); | |
| 121 | + return model; | |
| 122 | + } | |
| 123 | + | |
| 124 | + private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) { | |
| 125 | + // 1. 构建 message 列表 | |
| 126 | + List<Message> chatMessages = buildMessages(generateReqVO, systemMessage); | |
| 127 | + // 2. 构建 options 对象 | |
| 128 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); | |
| 129 | + ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens()); | |
| 130 | + return new Prompt(chatMessages, options); | |
| 131 | + } | |
| 132 | + | |
| 133 | + private List<Message> buildMessages(AiWriteGenerateReqVO generateReqVO, String systemMessage) { | |
| 134 | + List<Message> chatMessages = new ArrayList<>(); | |
| 135 | + if (StrUtil.isNotBlank(systemMessage)) { | |
| 136 | + // 1.1 角色设定 | |
| 137 | + chatMessages.add(new SystemMessage(systemMessage)); | |
| 138 | + } | |
| 139 | + // 1.2 用户输入 | |
| 140 | + chatMessages.add(new UserMessage(buildUserMessage(generateReqVO))); | |
| 141 | + return chatMessages; | |
| 142 | + } | |
| 143 | + | |
| 144 | + private String buildUserMessage(AiWriteGenerateReqVO generateReqVO) { | |
| 126 | 145 | String format = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_FORMAT, generateReqVO.getFormat()); |
| 127 | 146 | String tone = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getTone()); |
| 128 | 147 | String language = dictDataApi.getDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getLanguage()); | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
| ... | ... | @@ -18,12 +18,15 @@ import com.alibaba.cloud.ai.tongyi.TongYiConnectionProperties; |
| 18 | 18 | import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel; |
| 19 | 19 | import com.alibaba.cloud.ai.tongyi.chat.TongYiChatProperties; |
| 20 | 20 | import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; |
| 21 | +import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties; | |
| 21 | 22 | import com.alibaba.dashscope.aigc.generation.Generation; |
| 23 | +import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; | |
| 22 | 24 | import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; |
| 23 | 25 | import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; |
| 24 | 26 | import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; |
| 25 | 27 | import org.springframework.ai.autoconfigure.qianfan.QianFanChatProperties; |
| 26 | 28 | import org.springframework.ai.autoconfigure.qianfan.QianFanConnectionProperties; |
| 29 | +import org.springframework.ai.autoconfigure.qianfan.QianFanImageProperties; | |
| 27 | 30 | import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; |
| 28 | 31 | import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiChatProperties; |
| 29 | 32 | import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; |
| ... | ... | @@ -111,6 +114,10 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 111 | 114 | public ImageModel getDefaultImageModel(AiPlatformEnum platform) { |
| 112 | 115 | //noinspection EnhancedSwitchMigration |
| 113 | 116 | switch (platform) { |
| 117 | + case TONG_YI: | |
| 118 | + return SpringUtil.getBean(TongYiImagesModel.class); | |
| 119 | + case YI_YAN: | |
| 120 | + return SpringUtil.getBean(QianFanImageModel.class); | |
| 114 | 121 | case OPENAI: |
| 115 | 122 | return SpringUtil.getBean(OpenAiImageModel.class); |
| 116 | 123 | case STABLE_DIFFUSION: |
| ... | ... | @@ -124,14 +131,14 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 124 | 131 | public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url) { |
| 125 | 132 | //noinspection EnhancedSwitchMigration |
| 126 | 133 | switch (platform) { |
| 134 | + case TONG_YI: | |
| 135 | + return buildTongYiImagesModel(apiKey); | |
| 136 | + case YI_YAN: | |
| 137 | + return buildQianFanImageModel(apiKey); | |
| 127 | 138 | case OPENAI: |
| 128 | 139 | return buildOpenAiImageModel(apiKey, url); |
| 129 | 140 | case STABLE_DIFFUSION: |
| 130 | 141 | return buildStabilityAiImageModel(apiKey, url); |
| 131 | - case TONG_YI: | |
| 132 | - return SpringUtil.getBean(TongYiImagesModel.class); | |
| 133 | - case YI_YAN: | |
| 134 | - return buildQianFanImageModel(apiKey); | |
| 135 | 142 | default: |
| 136 | 143 | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| 137 | 144 | } |
| ... | ... | @@ -175,6 +182,14 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 175 | 182 | return new TongYiAutoConfiguration().tongYiChatClient(generation, chatOptions, connectionProperties); |
| 176 | 183 | } |
| 177 | 184 | |
| 185 | + private static TongYiImagesModel buildTongYiImagesModel(String key) { | |
| 186 | + ImageSynthesis imageSynthesis = SpringUtil.getBean(ImageSynthesis.class); | |
| 187 | + TongYiImagesProperties imagesOptions = SpringUtil.getBean(TongYiImagesProperties.class); | |
| 188 | + TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties(); | |
| 189 | + connectionProperties.setApiKey(key); | |
| 190 | + return new TongYiAutoConfiguration().tongYiImagesClient(imageSynthesis, imagesOptions, connectionProperties); | |
| 191 | + } | |
| 192 | + | |
| 178 | 193 | /** |
| 179 | 194 | * 可参考 {@link QianFanAutoConfiguration#qianFanChatModel(QianFanConnectionProperties, QianFanChatProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} |
| 180 | 195 | */ |
| ... | ... | @@ -188,6 +203,18 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 188 | 203 | } |
| 189 | 204 | |
| 190 | 205 | /** |
| 206 | + * 可参考 {@link QianFanAutoConfiguration#qianFanImageModel(QianFanConnectionProperties, QianFanImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} | |
| 207 | + */ | |
| 208 | + private QianFanImageModel buildQianFanImageModel(String key) { | |
| 209 | + List<String> keys = StrUtil.split(key, '|'); | |
| 210 | + Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); | |
| 211 | + String appKey = keys.get(0); | |
| 212 | + String secretKey = keys.get(1); | |
| 213 | + QianFanImageApi qianFanApi = new QianFanImageApi(appKey, secretKey); | |
| 214 | + return new QianFanImageModel(qianFanApi); | |
| 215 | + } | |
| 216 | + | |
| 217 | + /** | |
| 191 | 218 | * 可参考 {@link YudaoAiAutoConfiguration#deepSeekChatModel(YudaoAiProperties)} |
| 192 | 219 | */ |
| 193 | 220 | private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) { |
| ... | ... | @@ -246,8 +273,4 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 246 | 273 | return new StabilityAiImageModel(stabilityAiApi); |
| 247 | 274 | } |
| 248 | 275 | |
| 249 | - private QianFanImageModel buildQianFanImageModel(String key) { | |
| 250 | - List<String> keys = StrUtil.split(key, '|'); | |
| 251 | - return new QianFanImageModel(new QianFanImageApi(keys.get(0), keys.get(1))); | |
| 252 | - } | |
| 253 | 276 | } | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/OpenAiImageModelTests.java
| ... | ... | @@ -21,7 +21,7 @@ public class OpenAiImageModelTests { |
| 21 | 21 | "https://api.holdai.top", |
| 22 | 22 | "sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf", |
| 23 | 23 | RestClient.builder()); |
| 24 | - private final OpenAiImageModel imageClient = new OpenAiImageModel(imageApi); | |
| 24 | + private final OpenAiImageModel imageModel = new OpenAiImageModel(imageApi); | |
| 25 | 25 | |
| 26 | 26 | @Test |
| 27 | 27 | @Disabled |
| ... | ... | @@ -34,7 +34,7 @@ public class OpenAiImageModelTests { |
| 34 | 34 | ImagePrompt prompt = new ImagePrompt("中国长城!", options); |
| 35 | 35 | |
| 36 | 36 | // 方法调用 |
| 37 | - ImageResponse response = imageClient.call(prompt); | |
| 37 | + ImageResponse response = imageModel.call(prompt); | |
| 38 | 38 | // 打印结果 |
| 39 | 39 | System.out.println(response); |
| 40 | 40 | } | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/QianFanImageTests.java
| 1 | 1 | package cn.iocoder.yudao.framework.ai.image; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 3 | +import org.junit.jupiter.api.Disabled; | |
| 4 | 4 | import org.junit.jupiter.api.Test; |
| 5 | -import org.springframework.ai.image.ImageOptionsBuilder; | |
| 6 | 5 | import org.springframework.ai.image.ImagePrompt; |
| 7 | 6 | import org.springframework.ai.image.ImageResponse; |
| 8 | 7 | import org.springframework.ai.qianfan.QianFanImageModel; |
| 9 | 8 | import org.springframework.ai.qianfan.QianFanImageOptions; |
| 10 | -import org.springframework.ai.qianfan.api.QianFanApi; | |
| 11 | 9 | import org.springframework.ai.qianfan.api.QianFanImageApi; |
| 12 | 10 | |
| 11 | +import static cn.iocoder.yudao.framework.ai.image.StabilityAiImageModelTests.viewImage; | |
| 12 | + | |
| 13 | 13 | /** |
| 14 | - * 百度千帆 image | |
| 14 | + * {@link QianFanImageModel} 集成测试类 | |
| 15 | 15 | */ |
| 16 | 16 | public class QianFanImageTests { |
| 17 | 17 | |
| 18 | - @Test | |
| 19 | - public void callTest() { | |
| 20 | - // todo @芋艿 千帆sdk有个错误,暂时没找到问题 | |
| 21 | - QianFanImageApi qianFanImageApi = new QianFanImageApi( | |
| 22 | - "ghbbvbW2t7HK7WtYmEITAupm", "njJEr5AsQ5fkB3ucYYDjiQqsOZK20SGb"); | |
| 23 | - QianFanImageModel qianFanImageModel = new QianFanImageModel(qianFanImageApi); | |
| 18 | + private final QianFanImageApi imageApi = new QianFanImageApi( | |
| 19 | + "qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e"); | |
| 20 | + private final QianFanImageModel imageModel = new QianFanImageModel(imageApi); | |
| 24 | 21 | |
| 22 | + @Test | |
| 23 | + @Disabled | |
| 24 | + public void testCall() { | |
| 25 | + // 准备参数 | |
| 26 | + // 只支持 1024x1024、768x768、768x1024、1024x768、576x1024、1024x576 | |
| 25 | 27 | QianFanImageOptions imageOptions = QianFanImageOptions.builder() |
| 26 | - .withWidth(512) | |
| 27 | - .withHeight(512) | |
| 28 | + .withModel(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue()) | |
| 29 | + .withWidth(1024).withHeight(1024) | |
| 30 | + .withN(1) | |
| 28 | 31 | .build(); |
| 29 | - ImagePrompt imagePrompt = new ImagePrompt("薄涂炫酷少女头像,田野花朵盛开", imageOptions); | |
| 30 | - ImageResponse call = qianFanImageModel.call(imagePrompt); | |
| 31 | - System.err.println(JsonUtils.toJsonString(call)); | |
| 32 | - } | |
| 33 | - | |
| 34 | - @Test | |
| 35 | - public void call2Test() { | |
| 36 | - // 官方测试 test https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-qianfan/src/test/java/org/springframework/ai/qianfan/image/QianFanImageModelIT.java | |
| 37 | - var options = ImageOptionsBuilder.builder().withHeight(1024).withWidth(1024).build(); | |
| 38 | - var instructions = "薄涂炫酷少女头像,田野花朵盛开"; | |
| 39 | - | |
| 40 | - ImagePrompt imagePrompt = new ImagePrompt(instructions, options); | |
| 41 | - | |
| 42 | - QianFanImageApi qianFanImageApi = new QianFanImageApi( | |
| 43 | - "ghbbvbW2t7HK7WtYmEITAupm", "njJEr5AsQ5fkB3ucYYDjiQqsOZK20SGb"); | |
| 44 | - QianFanImageModel imageModel = new QianFanImageModel(qianFanImageApi); | |
| 45 | - ImageResponse imageResponse = imageModel.call(imagePrompt); | |
| 32 | + ImagePrompt prompt = new ImagePrompt("good", imageOptions); | |
| 33 | + | |
| 34 | + // 方法调用 | |
| 35 | + ImageResponse response = imageModel.call(prompt); | |
| 36 | + // 打印结果 | |
| 37 | + String b64Json = response.getResult().getOutput().getB64Json(); | |
| 38 | + System.out.println(response); | |
| 39 | + viewImage(b64Json); | |
| 46 | 40 | } |
| 47 | 41 | |
| 48 | 42 | } | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/StabilityAiImageModelTests.java
| ... | ... | @@ -24,7 +24,7 @@ public class StabilityAiImageModelTests { |
| 24 | 24 | |
| 25 | 25 | private final StabilityAiApi imageApi = new StabilityAiApi( |
| 26 | 26 | "sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx"); |
| 27 | - private final StabilityAiImageModel imageClient = new StabilityAiImageModel(imageApi); | |
| 27 | + private final StabilityAiImageModel imageModel = new StabilityAiImageModel(imageApi); | |
| 28 | 28 | |
| 29 | 29 | @Test |
| 30 | 30 | @Disabled |
| ... | ... | @@ -37,7 +37,7 @@ public class StabilityAiImageModelTests { |
| 37 | 37 | ImagePrompt prompt = new ImagePrompt("great wall", options); |
| 38 | 38 | |
| 39 | 39 | // 方法调用 |
| 40 | - ImageResponse response = imageClient.call(prompt); | |
| 40 | + ImageResponse response = imageModel.call(prompt); | |
| 41 | 41 | // 打印结果 |
| 42 | 42 | String b64Json = response.getResult().getOutput().getB64Json(); |
| 43 | 43 | System.out.println(response); | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.ai.image; | |
| 2 | + | |
| 3 | +import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; | |
| 4 | +import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; | |
| 5 | +import com.alibaba.dashscope.utils.Constants; | |
| 6 | +import org.junit.jupiter.api.Disabled; | |
| 7 | +import org.junit.jupiter.api.Test; | |
| 8 | +import org.springframework.ai.image.ImageOptions; | |
| 9 | +import org.springframework.ai.image.ImagePrompt; | |
| 10 | +import org.springframework.ai.image.ImageResponse; | |
| 11 | +import org.springframework.ai.openai.OpenAiImageOptions; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * {@link com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel} 集成测试类 | |
| 15 | + * | |
| 16 | + * @author fansili | |
| 17 | + */ | |
| 18 | +public class TongYiImagesModelTest { | |
| 19 | + | |
| 20 | + private final ImageSynthesis imageApi = new ImageSynthesis(); | |
| 21 | + private final TongYiImagesModel imageModel = new TongYiImagesModel(imageApi); | |
| 22 | + | |
| 23 | + static { | |
| 24 | + Constants.apiKey = "sk-Zsd81gZYg7"; | |
| 25 | + } | |
| 26 | + | |
| 27 | + @Test | |
| 28 | + @Disabled | |
| 29 | + public void imageCallTest() { | |
| 30 | + // 准备参数 | |
| 31 | + ImageOptions options = OpenAiImageOptions.builder() | |
| 32 | + .withModel(ImageSynthesis.Models.WANX_V1) | |
| 33 | + .withHeight(256).withWidth(256) | |
| 34 | + .build(); | |
| 35 | + ImagePrompt prompt = new ImagePrompt("中国长城!", options); | |
| 36 | + | |
| 37 | + // 方法调用 | |
| 38 | + ImageResponse response = imageModel.call(prompt); | |
| 39 | + // 打印结果 | |
| 40 | + System.out.println(response); | |
| 41 | + } | |
| 42 | + | |
| 43 | +} | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTests.java deleted
| 1 | -package cn.iocoder.yudao.framework.ai.image; | |
| 2 | - | |
| 3 | -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; | |
| 4 | -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam; | |
| 5 | -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult; | |
| 6 | -import com.alibaba.dashscope.exception.NoApiKeyException; | |
| 7 | -import com.alibaba.dashscope.utils.Constants; | |
| 8 | -import com.alibaba.fastjson.JSON; | |
| 9 | -import org.junit.jupiter.api.Test; | |
| 10 | - | |
| 11 | -import java.util.Map; | |
| 12 | - | |
| 13 | -// TODO @fan:改成 TongYiImagesModel 哈 | |
| 14 | -/** | |
| 15 | - * 通义万象 | |
| 16 | - */ | |
| 17 | -public class TongYiImagesModelTests { | |
| 18 | - | |
| 19 | - @Test | |
| 20 | - public void imageCallTest() throws NoApiKeyException { | |
| 21 | - // 设置 api key | |
| 22 | - Constants.apiKey = "sk-Zsd81gZYg7"; | |
| 23 | - ImageSynthesisParam param = | |
| 24 | - ImageSynthesisParam.builder() | |
| 25 | - .model(ImageSynthesis.Models.WANX_V1) | |
| 26 | - .n(4) | |
| 27 | - .size("1024*1024") | |
| 28 | - .prompt("雄鹰自由自在的在蓝天白云下飞翔") | |
| 29 | - .build(); | |
| 30 | - // 创建 ImageSynthesis | |
| 31 | - ImageSynthesis is = new ImageSynthesis(); | |
| 32 | - // 调用 call 生成 image | |
| 33 | - ImageSynthesisResult call = is.call(param); | |
| 34 | - System.err.println(JSON.toJSON(call)); | |
| 35 | - for (Map<String, String> result : call.getOutput().getResults()) { | |
| 36 | - System.err.println("地址: " + result.get("url")); | |
| 37 | - } | |
| 38 | - } | |
| 39 | -} |