Commit be8d9c1a5f195fea30c5df5a0e7d9e49ef720e06
1 parent
64b6b45b
【通义千问】重新对接阿里云通义前文。
Showing
14 changed files
with
160 additions
and
149 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/AiChatModalDO.java
| @@ -22,14 +22,24 @@ public class AiChatModalDO extends BaseDO { | @@ -22,14 +22,24 @@ public class AiChatModalDO extends BaseDO { | ||
| 22 | */ | 22 | */ |
| 23 | private Long id; | 23 | private Long id; |
| 24 | /** | 24 | /** |
| 25 | - * 模型名字 | 25 | + * 模型key |
| 26 | + */ | ||
| 27 | + private String modelKey; | ||
| 28 | + /** | ||
| 29 | + * 模型类型 参考:{@link cn.iocoder.yudao.framework.ai.AiPlatformEnum} | ||
| 26 | */ | 30 | */ |
| 27 | - private Long modelName; | 31 | + private String modelPlatform; |
| 28 | /** | 32 | /** |
| 29 | - * 模型类型(qianwen、yiyan、xinghuo、openai) | 33 | + * 模型类型 |
| 34 | + * {@link cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel} | ||
| 35 | + * {@link cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatModel} | ||
| 30 | */ | 36 | */ |
| 31 | private String modelType; | 37 | private String modelType; |
| 32 | /** | 38 | /** |
| 39 | + * 模型名字 | ||
| 40 | + */ | ||
| 41 | + private String modelName; | ||
| 42 | + /** | ||
| 33 | * 模型照片 | 43 | * 模型照片 |
| 34 | */ | 44 | */ |
| 35 | private String modalImage; | 45 | private String modalImage; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/vo/AiChatModalListRes.java
| @@ -20,7 +20,7 @@ public class AiChatModalListRes { | @@ -20,7 +20,7 @@ public class AiChatModalListRes { | ||
| 20 | /** | 20 | /** |
| 21 | * 模型名字 | 21 | * 模型名字 |
| 22 | */ | 22 | */ |
| 23 | - private Long modelName; | 23 | + private String modelName; |
| 24 | /** | 24 | /** |
| 25 | * 模型类型(qianwen、yiyan、xinghuo、openai) | 25 | * 模型类型(qianwen、yiyan、xinghuo、openai) |
| 26 | */ | 26 | */ |
yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/chat-modal.http
0 → 100644
| 1 | + | ||
| 2 | + | ||
| 3 | +### chat call | ||
| 4 | +GET {{baseUrl}}/ai/chat/modal/list | ||
| 5 | +Authorization: {{token}} | ||
| 6 | + | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +### chat call | ||
| 10 | +PUT {{baseUrl}}/ai/chat/modal | ||
| 11 | +Content-Type: application/json | ||
| 12 | +Authorization: {{token}} | ||
| 13 | + | ||
| 14 | +{ | ||
| 15 | + "modelName": "小红书Ai写作大模型", | ||
| 16 | + "modelType": "yiyan", | ||
| 17 | + "modalImage": "", | ||
| 18 | + "modelConfig": "" | ||
| 19 | +} |
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
| @@ -110,9 +110,9 @@ | @@ -110,9 +110,9 @@ | ||
| 110 | </dependency> | 110 | </dependency> |
| 111 | <!-- 阿里云 通义千问 --> | 111 | <!-- 阿里云 通义千问 --> |
| 112 | <dependency> | 112 | <dependency> |
| 113 | - <groupId>com.aliyun</groupId> | ||
| 114 | - <artifactId>broadscope-bailian-sdk-java</artifactId> | ||
| 115 | - <version>1.3.0</version> | 113 | + <groupId>com.alibaba</groupId> |
| 114 | + <artifactId>dashscope-sdk-java</artifactId> | ||
| 115 | + <version>2.11.0</version> | ||
| 116 | </dependency> | 116 | </dependency> |
| 117 | <dependency> | 117 | <dependency> |
| 118 | <groupId>org.apache.httpcomponents</groupId> | 118 | <groupId>org.apache.httpcomponents</groupId> |
| @@ -129,6 +129,11 @@ | @@ -129,6 +129,11 @@ | ||
| 129 | <groupId>com.fasterxml.jackson.core</groupId> | 129 | <groupId>com.fasterxml.jackson.core</groupId> |
| 130 | <artifactId>jackson-databind</artifactId> | 130 | <artifactId>jackson-databind</artifactId> |
| 131 | </dependency> | 131 | </dependency> |
| 132 | + <dependency> | ||
| 133 | + <groupId>com.alibaba.fastjson2</groupId> | ||
| 134 | + <artifactId>fastjson2</artifactId> | ||
| 135 | + <version>2.0.45</version> | ||
| 136 | + </dependency> | ||
| 132 | <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --> | 137 | <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --> |
| 133 | <dependency> | 138 | <dependency> |
| 134 | <groupId>com.squareup.okhttp3</groupId> | 139 | <groupId>com.squareup.okhttp3</groupId> |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java
| @@ -4,13 +4,7 @@ import lombok.AllArgsConstructor; | @@ -4,13 +4,7 @@ import lombok.AllArgsConstructor; | ||
| 4 | import lombok.Getter; | 4 | import lombok.Getter; |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| 7 | - * 讯飞星火 模型 | ||
| 8 | - * | ||
| 9 | - * 文档地址:https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E | ||
| 10 | - * | ||
| 11 | - * 1tokens 约等于1.5个中文汉字 或者 0.8个英文单词 | ||
| 12 | - * 星火V1.5支持[搜索]内置插件;星火V2.0、V3.0和V3.5支持[搜索]、[天气]、[日期]、[诗词]、[字词]、[股票]六个内置插件 | ||
| 13 | - * 星火V3.5 现已支持system、Function Call 功能。 | 7 | + * ai 模型平台 |
| 14 | * | 8 | * |
| 15 | * author: fansili | 9 | * author: fansili |
| 16 | * time: 2024/3/11 10:12 | 10 | * time: 2024/3/11 10:12 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenChatClient.java
| 1 | package cn.iocoder.yudao.framework.ai.chatqianwen; | 1 | package cn.iocoder.yudao.framework.ai.chatqianwen; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.util.IdUtil; | ||
| 4 | -import cn.hutool.json.JSONUtil; | ||
| 5 | import cn.iocoder.yudao.framework.ai.chat.*; | 3 | import cn.iocoder.yudao.framework.ai.chat.*; |
| 6 | -import cn.iocoder.yudao.framework.ai.chat.messages.MessageType; | ||
| 7 | import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; | 4 | import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; |
| 8 | import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; | 5 | import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; |
| 9 | import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenApi; | 6 | import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenApi; |
| 7 | +import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenChatCompletionRequest; | ||
| 10 | import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; | 8 | import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; |
| 11 | -import com.aliyun.broadscope.bailian.sdk.models.*; | 9 | +import com.alibaba.dashscope.aigc.generation.GenerationResult; |
| 10 | +import com.alibaba.dashscope.aigc.generation.models.QwenParam; | ||
| 11 | +import com.alibaba.dashscope.common.Message; | ||
| 12 | +import io.reactivex.Flowable; | ||
| 12 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 13 | import org.springframework.http.ResponseEntity; | 14 | import org.springframework.http.ResponseEntity; |
| 14 | import org.springframework.retry.RetryCallback; | 15 | import org.springframework.retry.RetryCallback; |
| @@ -23,9 +24,9 @@ import java.util.stream.Collectors; | @@ -23,9 +24,9 @@ import java.util.stream.Collectors; | ||
| 23 | 24 | ||
| 24 | /** | 25 | /** |
| 25 | * 阿里 通义千问 client | 26 | * 阿里 通义千问 client |
| 26 | - * | 27 | + * <p> |
| 27 | * 文档地址:https://help.aliyun.com/document_detail/2587494.html?spm=a2c4g.2587492.0.0.53f33c566sXskp | 28 | * 文档地址:https://help.aliyun.com/document_detail/2587494.html?spm=a2c4g.2587492.0.0.53f33c566sXskp |
| 28 | - * | 29 | + * <p> |
| 29 | * author: fansili | 30 | * author: fansili |
| 30 | * time: 2024/3/13 21:06 | 31 | * time: 2024/3/13 21:06 |
| 31 | */ | 32 | */ |
| @@ -37,7 +38,9 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | @@ -37,7 +38,9 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | ||
| 37 | private QianWenOptions qianWenOptions; | 38 | private QianWenOptions qianWenOptions; |
| 38 | 39 | ||
| 39 | 40 | ||
| 40 | - public QianWenChatClient() {} | 41 | + public QianWenChatClient() { |
| 42 | + } | ||
| 43 | + | ||
| 41 | public QianWenChatClient(QianWenApi qianWenApi) { | 44 | public QianWenChatClient(QianWenApi qianWenApi) { |
| 42 | this.qianWenApi = qianWenApi; | 45 | this.qianWenApi = qianWenApi; |
| 43 | } | 46 | } |
| @@ -59,7 +62,7 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | @@ -59,7 +62,7 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | ||
| 59 | public <T extends Object, E extends Throwable> void onError(RetryContext context, | 62 | public <T extends Object, E extends Throwable> void onError(RetryContext context, |
| 60 | RetryCallback<T, E> callback, Throwable throwable) { | 63 | RetryCallback<T, E> callback, Throwable throwable) { |
| 61 | log.warn("重试异常:" + context.getRetryCount(), throwable); | 64 | log.warn("重试异常:" + context.getRetryCount(), throwable); |
| 62 | - }; | 65 | + } |
| 63 | }) | 66 | }) |
| 64 | .build(); | 67 | .build(); |
| 65 | 68 | ||
| @@ -68,21 +71,21 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | @@ -68,21 +71,21 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | ||
| 68 | return this.retryTemplate.execute(ctx -> { | 71 | return this.retryTemplate.execute(ctx -> { |
| 69 | // ctx 会有重试的信息 | 72 | // ctx 会有重试的信息 |
| 70 | // 创建 request 请求,stream模式需要供应商支持 | 73 | // 创建 request 请求,stream模式需要供应商支持 |
| 71 | - CompletionsRequest request = this.createRequest(prompt, false); | 74 | + QianWenChatCompletionRequest request = this.createRequest(prompt, false); |
| 72 | // 调用 callWithFunctionSupport 发送请求 | 75 | // 调用 callWithFunctionSupport 发送请求 |
| 73 | - ResponseEntity<CompletionsResponse> responseEntity = qianWenApi.chatCompletionEntity(request); | 76 | + ResponseEntity<GenerationResult> responseEntity = qianWenApi.chatCompletionEntity(request); |
| 74 | // 获取结果封装 chatCompletion | 77 | // 获取结果封装 chatCompletion |
| 75 | - CompletionsResponse response = responseEntity.getBody(); | ||
| 76 | - if (!response.isSuccess()) { | ||
| 77 | - return new ChatResponse(List.of(new Generation(String.format("failed to create completion, requestId: %s, code: %s, message: %s\n", | ||
| 78 | - response.getRequestId(), response.getCode(), response.getMessage())))); | ||
| 79 | - } | 78 | + GenerationResult response = responseEntity.getBody(); |
| 79 | +// if (!response.isSuccess()) { | ||
| 80 | +// return new ChatResponse(List.of(new Generation(String.format("failed to create completion, requestId: %s, code: %s, message: %s\n", | ||
| 81 | +// response.getRequestId(), response.getCode(), response.getMessage())))); | ||
| 82 | +// } | ||
| 80 | // 转换为 Generation 返回 | 83 | // 转换为 Generation 返回 |
| 81 | - return new ChatResponse(List.of(new Generation(response.getData().getText()))); | 84 | + return new ChatResponse(List.of(new Generation(response.getOutput().getText()))); |
| 82 | }); | 85 | }); |
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | - private CompletionsRequest createRequest(Prompt prompt, boolean stream) { | 88 | + private QianWenChatCompletionRequest createRequest(Prompt prompt, boolean stream) { |
| 86 | // 两个都为null 则没有配置文件 | 89 | // 两个都为null 则没有配置文件 |
| 87 | if (qianWenOptions == null && prompt.getOptions() == null) { | 90 | if (qianWenOptions == null && prompt.getOptions() == null) { |
| 88 | throw new ChatException("ChatOptions 未配置参数!"); | 91 | throw new ChatException("ChatOptions 未配置参数!"); |
| @@ -93,61 +96,40 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | @@ -93,61 +96,40 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { | ||
| 93 | options = (ChatOptions) prompt.getOptions(); | 96 | options = (ChatOptions) prompt.getOptions(); |
| 94 | } | 97 | } |
| 95 | // Prompt 里面是一个 ChatOptions,用户可以随意传入,这里做一下判断 | 98 | // Prompt 里面是一个 ChatOptions,用户可以随意传入,这里做一下判断 |
| 96 | - if (!(options instanceof QianWenOptions)) { | 99 | + if (!(options instanceof QianWenOptions qianWenOptions)) { |
| 97 | throw new ChatException("Prompt 传入的不是 QianWenOptions!"); | 100 | throw new ChatException("Prompt 传入的不是 QianWenOptions!"); |
| 98 | } | 101 | } |
| 99 | - QianWenOptions qianWenOptions = (QianWenOptions) options; | ||
| 100 | - // 需要额外处理 | ||
| 101 | - if (!stream) { | ||
| 102 | - // 如果不需要 stream 输出,那么需要将这个设置为false,不然只会输出最后几个文字 | ||
| 103 | - if (qianWenOptions.getParameters() == null) { | ||
| 104 | - qianWenOptions.setParameters(new CompletionsRequest.Parameter().setIncrementalOutput(false)); | ||
| 105 | - } else { | ||
| 106 | - qianWenOptions.getParameters().setIncrementalOutput(false); | ||
| 107 | - } | ||
| 108 | - } else { | ||
| 109 | - // 如果不需要 stream 输出,设置为true这样不会输出累加内容 | ||
| 110 | - if (qianWenOptions.getParameters() == null) { | ||
| 111 | - qianWenOptions.setParameters(new CompletionsRequest.Parameter().setIncrementalOutput(true)); | ||
| 112 | - } else { | ||
| 113 | - qianWenOptions.getParameters().setIncrementalOutput(true); | ||
| 114 | - } | ||
| 115 | - } | ||
| 116 | - | ||
| 117 | - // 创建request | ||
| 118 | - return new CompletionsRequest() | ||
| 119 | - // 请求唯一标识,请确保RequestId不重复。 | ||
| 120 | - .setRequestId(IdUtil.getSnowflakeNextIdStr()) | ||
| 121 | - // 设置 appid | ||
| 122 | - .setAppId(qianWenOptions.getAppId()) | ||
| 123 | - .setMessages(prompt.getInstructions().stream().map(m -> { | ||
| 124 | - // 转换成 千问 对于的请求message | ||
| 125 | - if (MessageType.USER == m.getMessageType()) { | ||
| 126 | - return new ChatUserMessage(m.getContent()); | ||
| 127 | - } else if (MessageType.SYSTEM == m.getMessageType()) { | ||
| 128 | - return new ChatSystemMessage(m.getContent()); | ||
| 129 | - } else if (MessageType.ASSISTANT == m.getMessageType()) { | ||
| 130 | - return new ChatAssistantMessage(m.getContent()); | ||
| 131 | - } | ||
| 132 | - throw new ChatException(String.format("存在不能适配的消息! %s", JSONUtil.toJsonPrettyStr(m))); | 102 | + return (QianWenChatCompletionRequest) QianWenChatCompletionRequest.builder() |
| 103 | + .model(qianWenApi.getQianWenChatModal().getValue()) | ||
| 104 | + .apiKey(qianWenApi.getApiKey()) | ||
| 105 | + .messages(prompt.getInstructions().stream().map(m -> { | ||
| 106 | + Message message = new Message(); | ||
| 107 | + message.setRole(m.getMessageType().getValue()); | ||
| 108 | + message.setContent(m.getContent()); | ||
| 109 | + return message; | ||
| 133 | }).collect(Collectors.toList())) | 110 | }).collect(Collectors.toList())) |
| 134 | - // 返回choice message结果 | ||
| 135 | - .setParameters(qianWenOptions.getParameters()) | ||
| 136 | - // 设置 ChatOptions 里面公共的参数 | ||
| 137 | - .setTopP(options.getTopP() == null ? null : options.getTopP().doubleValue()) | ||
| 138 | - // 设置输出方式 | ||
| 139 | - .setStream(stream); | 111 | + .resultFormat(QwenParam.ResultFormat.MESSAGE) |
| 112 | + // 动态改变的三个参数 | ||
| 113 | + .topP(Double.valueOf(qianWenOptions.getTopP())) | ||
| 114 | + .topK(qianWenOptions.getTopK()) | ||
| 115 | + .temperature(qianWenOptions.getTemperature()) | ||
| 116 | + .incrementalOutput(true) | ||
| 117 | + .build(); | ||
| 140 | } | 118 | } |
| 141 | 119 | ||
| 142 | @Override | 120 | @Override |
| 143 | public Flux<ChatResponse> stream(Prompt prompt) { | 121 | public Flux<ChatResponse> stream(Prompt prompt) { |
| 144 | // ctx 会有重试的信息 | 122 | // ctx 会有重试的信息 |
| 145 | // 创建 request 请求,stream模式需要供应商支持 | 123 | // 创建 request 请求,stream模式需要供应商支持 |
| 146 | - CompletionsRequest request = this.createRequest(prompt, true); | 124 | + QianWenChatCompletionRequest request = this.createRequest(prompt, true); |
| 147 | // 调用 callWithFunctionSupport 发送请求 | 125 | // 调用 callWithFunctionSupport 发送请求 |
| 148 | - Flux<CompletionsResponse> response = this.qianWenApi.chatCompletionStream(request); | ||
| 149 | - return response.map(res -> { | ||
| 150 | - return new ChatResponse(List.of(new Generation(res.getData().getText()))); | ||
| 151 | - }); | 126 | + Flowable<GenerationResult> responseResult = this.qianWenApi.chatCompletionStream(request); |
| 127 | + return Flux.create(fluxSink -> | ||
| 128 | + responseResult.subscribe( | ||
| 129 | + value -> fluxSink.next(new ChatResponse(List.of(new Generation(value.getOutput().getText())))), | ||
| 130 | + error -> fluxSink.error(error), | ||
| 131 | + () -> fluxSink.complete() | ||
| 132 | + ) | ||
| 133 | + ); | ||
| 152 | } | 134 | } |
| 153 | } | 135 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenChatModal.java
| @@ -7,6 +7,7 @@ import lombok.Getter; | @@ -7,6 +7,7 @@ import lombok.Getter; | ||
| 7 | * 千问 chat 模型 | 7 | * 千问 chat 模型 |
| 8 | * | 8 | * |
| 9 | * 模型地址:https://help.aliyun.com/document_detail/2712576.html | 9 | * 模型地址:https://help.aliyun.com/document_detail/2712576.html |
| 10 | + * 模型介绍:https://help.aliyun.com/document_detail/2666503.html?spm=a2c4g.2701795.0.0.26eb34dfKzcWN4 | ||
| 10 | * | 11 | * |
| 11 | * @author fansili | 12 | * @author fansili |
| 12 | * @time 2024/4/26 10:15 | 13 | * @time 2024/4/26 10:15 |
| @@ -16,18 +17,22 @@ import lombok.Getter; | @@ -16,18 +17,22 @@ import lombok.Getter; | ||
| 16 | @Getter | 17 | @Getter |
| 17 | public enum QianWenChatModal { | 18 | public enum QianWenChatModal { |
| 18 | 19 | ||
| 19 | - qwen_turbo("通义千问超大规模语言模型", "qwen-turbo"), | ||
| 20 | - qwen_plus("通义千问超大规模语言模型增强版", "qwen-plus"), | ||
| 21 | - qwen_max("通义千问千亿级别超大规模语言模型", "qwen-max"), | ||
| 22 | - qwen_max_0403("通义千问千亿级别超大规模语言模型-0403", "qwen-max-0403"), | ||
| 23 | - qwen_max_0107("通义千问千亿级别超大规模语言模型-0107", "qwen-max-0107"), | ||
| 24 | - qwen_max_1201("通义千问千亿级别超大规模语言模型-1201", "qwen-max-1201"), | ||
| 25 | - qwen_max_longcontext("通义千问千亿级别超大规模语言模型-28k tokens", "qwen-max-longcontext"), | 20 | + // 千问付费模型 |
| 21 | + QWEN_TURBO("通义千问超大规模语言模型", "qwen-turbo"), | ||
| 22 | + QWEN_PLUS("通义千问超大规模语言模型增强版", "qwen-plus"), | ||
| 23 | + QWEN_MAX("通义千问千亿级别超大规模语言模型", "qwen-max"), | ||
| 24 | + QWEN_MAX_0403("通义千问千亿级别超大规模语言模型-0403", "qwen-max-0403"), | ||
| 25 | + QWEN_MAX_0107("通义千问千亿级别超大规模语言模型-0107", "qwen-max-0107"), | ||
| 26 | + QWEN_MAX_1201("通义千问千亿级别超大规模语言模型-1201", "qwen-max-1201"), | ||
| 27 | + QWEN_MAX_LONGCONTEXT("通义千问千亿级别超大规模语言模型-28k tokens", "qwen-max-longcontext"), | ||
| 28 | + | ||
| 29 | + // 开源模型 | ||
| 30 | + // https://help.aliyun.com/document_detail/2666503.html?spm=a2c4g.2701795.0.0.26eb34dfKzcWN4 | ||
| 31 | + QWEN_72B_CHAT("通义千问1.5对外开源的72B规模参数量的经过人类指令对齐的chat模型", "qwen-72b-chat"), | ||
| 26 | 32 | ||
| 27 | ; | 33 | ; |
| 28 | 34 | ||
| 29 | private String name; | 35 | private String name; |
| 30 | 36 | ||
| 31 | private String value; | 37 | private String value; |
| 32 | - | ||
| 33 | } | 38 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenOptions.java
| 1 | package cn.iocoder.yudao.framework.ai.chatqianwen; | 1 | package cn.iocoder.yudao.framework.ai.chatqianwen; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; | 3 | import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; |
| 4 | -import com.aliyun.broadscope.bailian.sdk.models.CompletionsRequest; | ||
| 5 | import lombok.Data; | 4 | import lombok.Data; |
| 6 | import lombok.experimental.Accessors; | 5 | import lombok.experimental.Accessors; |
| 7 | 6 | ||
| @@ -34,22 +33,18 @@ public class QianWenOptions implements ChatOptions { | @@ -34,22 +33,18 @@ public class QianWenOptions implements ChatOptions { | ||
| 34 | * 默认值为0.8。注意,取值不要大于等于1 | 33 | * 默认值为0.8。注意,取值不要大于等于1 |
| 35 | */ | 34 | */ |
| 36 | private Float topP; | 35 | private Float topP; |
| 37 | - /** | ||
| 38 | - * 模型参数设置。 | ||
| 39 | - */ | ||
| 40 | - private CompletionsRequest.Parameter parameters = new CompletionsRequest.Parameter(); | ||
| 41 | 36 | ||
| 42 | // | 37 | // |
| 43 | // 适配 ChatOptions | 38 | // 适配 ChatOptions |
| 44 | 39 | ||
| 45 | @Override | 40 | @Override |
| 46 | public Float getTemperature() { | 41 | public Float getTemperature() { |
| 47 | - return Float.parseFloat(this.parameters.getTemperature().toString()); | 42 | + return null; |
| 48 | } | 43 | } |
| 49 | 44 | ||
| 50 | @Override | 45 | @Override |
| 51 | public void setTemperature(Float temperature) { | 46 | public void setTemperature(Float temperature) { |
| 52 | - this.parameters.setTemperature(Double.valueOf(temperature.toString())); | 47 | + |
| 53 | } | 48 | } |
| 54 | 49 | ||
| 55 | @Override | 50 | @Override |
| @@ -59,12 +54,12 @@ public class QianWenOptions implements ChatOptions { | @@ -59,12 +54,12 @@ public class QianWenOptions implements ChatOptions { | ||
| 59 | 54 | ||
| 60 | @Override | 55 | @Override |
| 61 | public Integer getTopK() { | 56 | public Integer getTopK() { |
| 62 | - return this.parameters.getTopK(); | 57 | + return null; |
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | @Override | 60 | @Override |
| 66 | public void setTopK(Integer topK) { | 61 | public void setTopK(Integer topK) { |
| 67 | - this.parameters.setTopK(topK); | 62 | + |
| 68 | } | 63 | } |
| 69 | 64 | ||
| 70 | @Data | 65 | @Data |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenApi.java
| 1 | package cn.iocoder.yudao.framework.ai.chatqianwen.api; | 1 | package cn.iocoder.yudao.framework.ai.chatqianwen.api; |
| 2 | 2 | ||
| 3 | -import com.aliyun.broadscope.bailian.sdk.AccessTokenClient; | ||
| 4 | -import com.aliyun.broadscope.bailian.sdk.ApplicationClient; | ||
| 5 | -import com.aliyun.broadscope.bailian.sdk.models.CompletionsRequest; | ||
| 6 | -import com.aliyun.broadscope.bailian.sdk.models.CompletionsResponse; | 3 | +import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatModal; |
| 4 | +import cn.iocoder.yudao.framework.ai.exception.AiException; | ||
| 5 | +import com.alibaba.dashscope.aigc.generation.Generation; | ||
| 6 | +import com.alibaba.dashscope.aigc.generation.GenerationResult; | ||
| 7 | +import com.alibaba.dashscope.common.Message; | ||
| 8 | +import com.alibaba.dashscope.common.Role; | ||
| 9 | +import com.alibaba.dashscope.exception.InputRequiredException; | ||
| 10 | +import com.alibaba.dashscope.exception.NoApiKeyException; | ||
| 11 | +import io.reactivex.Flowable; | ||
| 7 | import lombok.Getter; | 12 | import lombok.Getter; |
| 8 | import org.springframework.http.HttpStatusCode; | 13 | import org.springframework.http.HttpStatusCode; |
| 9 | import org.springframework.http.ResponseEntity; | 14 | import org.springframework.http.ResponseEntity; |
| 10 | -import reactor.core.publisher.Flux; | ||
| 11 | 15 | ||
| 12 | // TODO done @fansili:是不是挪到 api 包里?按照 spring ai 的结构;根目录只放 client 和 options | 16 | // TODO done @fansili:是不是挪到 api 包里?按照 spring ai 的结构;根目录只放 client 和 options |
| 17 | + | ||
| 13 | /** | 18 | /** |
| 14 | * 阿里 通义千问 | 19 | * 阿里 通义千问 |
| 15 | - * | ||
| 16 | - * https://www.aliyun.com/search?k=%E9%80%9A%E4%B9%89%E5%A4%A7%E6%A8%A1%E5%9E%8B&scene=all | ||
| 17 | - * | 20 | + * <p> |
| 18 | * author: fansili | 21 | * author: fansili |
| 19 | * time: 2024/3/13 21:09 | 22 | * time: 2024/3/13 21:09 |
| 20 | */ | 23 | */ |
| 21 | @Getter | 24 | @Getter |
| 22 | public class QianWenApi { | 25 | public class QianWenApi { |
| 23 | 26 | ||
| 24 | - /** | ||
| 25 | - * accessKeyId、accessKeySecret、agentKey、appId 获取方式如下链接 | ||
| 26 | - * https://help.aliyun.com/document_detail/2587494.html?spm=a2c4g.2587492.0.0.53f33c566sXskp | ||
| 27 | - */ | ||
| 28 | - private String accessKeyId; | ||
| 29 | - private String accessKeySecret; | ||
| 30 | - private String agentKey; | ||
| 31 | - private String appId; | ||
| 32 | - private String endpoint = "bailian.cn-beijing.aliyuncs.com"; | ||
| 33 | - private String token; | ||
| 34 | - private ApplicationClient client; | ||
| 35 | - | ||
| 36 | - public QianWenApi(String accessKeyId, String accessKeySecret, String agentKey, String endpoint) { | ||
| 37 | - this.accessKeyId = accessKeyId; | ||
| 38 | - this.accessKeySecret = accessKeySecret; | ||
| 39 | - this.agentKey = agentKey; | ||
| 40 | - | ||
| 41 | - if (endpoint != null) { | ||
| 42 | - this.endpoint = endpoint; | ||
| 43 | - } | 27 | + // api key 获取地址:https://bailian.console.aliyun.com/?spm=5176.28197581.0.0.38db29a4G3GcVb&apiKey=1#/api-key |
| 28 | + private String apiKey = "sk-Zsd81gZYg7"; | ||
| 29 | + private Generation gen = new Generation(); | ||
| 30 | + private QianWenChatModal qianWenChatModal; | ||
| 44 | 31 | ||
| 45 | - // 获取token | ||
| 46 | - AccessTokenClient accessTokenClient = new AccessTokenClient(accessKeyId, accessKeySecret, agentKey); | ||
| 47 | - token = accessTokenClient.getToken(); | ||
| 48 | - // 构建client | ||
| 49 | - client = ApplicationClient.builder() | ||
| 50 | - .token(token) | ||
| 51 | - .build(); | 32 | + public QianWenApi(String apiKey, QianWenChatModal qianWenChatModal) { |
| 33 | + this.apiKey = apiKey; | ||
| 34 | + this.qianWenChatModal = qianWenChatModal; | ||
| 52 | } | 35 | } |
| 53 | 36 | ||
| 54 | - public ResponseEntity<CompletionsResponse> chatCompletionEntity(CompletionsRequest request) { | ||
| 55 | - // 发送请求 | ||
| 56 | - CompletionsResponse response = client.completions(request); | 37 | + public ResponseEntity<GenerationResult> chatCompletionEntity(QianWenChatCompletionRequest request) { |
| 38 | + Message userMsg = Message.builder().role(Role.USER.getValue()).content("用萝卜、土豆、茄子做饭,给我个菜谱").build(); | ||
| 39 | + | ||
| 40 | + GenerationResult call; | ||
| 41 | + try { | ||
| 42 | + call = gen.call(request); | ||
| 43 | + } catch (NoApiKeyException e) { | ||
| 44 | + throw new AiException("没有找到apiKey!" + e.getMessage()); | ||
| 45 | + } catch (InputRequiredException e) { | ||
| 46 | + throw new AiException("chat缺少必填字段!" + e.getMessage()); | ||
| 47 | + } | ||
| 57 | // 阿里云的这个 http code 随便设置,外面判断是否成功用的 CompletionsResponse.isSuccess | 48 | // 阿里云的这个 http code 随便设置,外面判断是否成功用的 CompletionsResponse.isSuccess |
| 58 | - return new ResponseEntity<>(response, HttpStatusCode.valueOf(200)); | 49 | + return new ResponseEntity<>(call, HttpStatusCode.valueOf(200)); |
| 59 | } | 50 | } |
| 60 | 51 | ||
| 61 | - public Flux<CompletionsResponse> chatCompletionStream(CompletionsRequest request) { | ||
| 62 | - return client.streamCompletions(request); | 52 | + public Flowable<GenerationResult> chatCompletionStream(QianWenChatCompletionRequest request) { |
| 53 | + Flowable<GenerationResult> resultFlowable; | ||
| 54 | + try { | ||
| 55 | + resultFlowable = gen.streamCall(request); | ||
| 56 | + } catch (NoApiKeyException e) { | ||
| 57 | + throw new AiException("没有找到apiKey!" + e.getMessage()); | ||
| 58 | + } catch (InputRequiredException e) { | ||
| 59 | + throw new AiException("chat缺少必填字段!" + e.getMessage()); | ||
| 60 | + } | ||
| 61 | + return resultFlowable; | ||
| 63 | } | 62 | } |
| 64 | } | 63 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenChatCompletion.java
| 1 | package cn.iocoder.yudao.framework.ai.chatqianwen.api; | 1 | package cn.iocoder.yudao.framework.ai.chatqianwen.api; |
| 2 | 2 | ||
| 3 | -import com.aliyun.broadscope.bailian.sdk.models.CompletionsResponse; | ||
| 4 | 3 | ||
| 5 | /** | 4 | /** |
| 6 | * author: fansili | 5 | * author: fansili |
| 7 | * time: 2024/3/13 21:07 | 6 | * time: 2024/3/13 21:07 |
| 8 | */ | 7 | */ |
| 9 | -public class QianWenChatCompletion extends CompletionsResponse { | 8 | +public class QianWenChatCompletion { |
| 10 | } | 9 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenChatCompletionRequest.java
| 1 | package cn.iocoder.yudao.framework.ai.chatqianwen.api; | 1 | package cn.iocoder.yudao.framework.ai.chatqianwen.api; |
| 2 | 2 | ||
| 3 | -import com.aliyun.broadscope.bailian.sdk.models.ChatRequestMessage; | ||
| 4 | -import com.aliyun.broadscope.bailian.sdk.models.ChatUserMessage; | 3 | +import com.alibaba.dashscope.aigc.generation.models.QwenParam; |
| 5 | 4 | ||
| 6 | /** | 5 | /** |
| 6 | + * 千问 | ||
| 7 | + * | ||
| 7 | * author: fansili | 8 | * author: fansili |
| 8 | * time: 2024/3/13 21:07 | 9 | * time: 2024/3/13 21:07 |
| 9 | */ | 10 | */ |
| 10 | -public class QianWenChatCompletionRequest extends ChatRequestMessage { | 11 | +public class QianWenChatCompletionRequest extends QwenParam { |
| 11 | 12 | ||
| 13 | + protected QianWenChatCompletionRequest(QwenParamBuilder<?, ?> b) { | ||
| 14 | + super(b); | ||
| 15 | + } | ||
| 12 | } | 16 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
| 1 | package cn.iocoder.yudao.framework.ai.config; | 1 | package cn.iocoder.yudao.framework.ai.config; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatClient; | 3 | import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatClient; |
| 4 | +import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatModal; | ||
| 4 | import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenOptions; | 5 | import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenOptions; |
| 5 | import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenApi; | 6 | import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenApi; |
| 6 | import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatClient; | 7 | import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatClient; |
| @@ -45,10 +46,8 @@ public class YudaoAiAutoConfiguration { | @@ -45,10 +46,8 @@ public class YudaoAiAutoConfiguration { | ||
| 45 | YudaoAiProperties.QianWenProperties qianWenProperties = yudaoAiProperties.getQianwen(); | 46 | YudaoAiProperties.QianWenProperties qianWenProperties = yudaoAiProperties.getQianwen(); |
| 46 | return new QianWenChatClient( | 47 | return new QianWenChatClient( |
| 47 | new QianWenApi( | 48 | new QianWenApi( |
| 48 | - qianWenProperties.getAccessKeyId(), | ||
| 49 | - qianWenProperties.getAccessKeySecret(), | ||
| 50 | qianWenProperties.getAgentKey(), | 49 | qianWenProperties.getAgentKey(), |
| 51 | - qianWenProperties.getEndpoint() | 50 | + QianWenChatModal.QWEN_72B_CHAT |
| 52 | ), | 51 | ), |
| 53 | new QianWenOptions() | 52 | new QianWenOptions() |
| 54 | .setAppId(qianWenProperties.getAppId()) | 53 | .setAppId(qianWenProperties.getAppId()) |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/api/MidjourneyInteractionsApi.java
| @@ -2,13 +2,13 @@ package cn.iocoder.yudao.framework.ai.midjourney.api; | @@ -2,13 +2,13 @@ package cn.iocoder.yudao.framework.ai.midjourney.api; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.StrUtil; | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; | 4 | import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; |
| 5 | -import cn.iocoder.yudao.framework.ai.midjourney.util.MidjourneyUtil; | ||
| 6 | import cn.iocoder.yudao.framework.ai.midjourney.api.req.AttachmentsReq; | 5 | import cn.iocoder.yudao.framework.ai.midjourney.api.req.AttachmentsReq; |
| 7 | import cn.iocoder.yudao.framework.ai.midjourney.api.req.DescribeReq; | 6 | import cn.iocoder.yudao.framework.ai.midjourney.api.req.DescribeReq; |
| 8 | import cn.iocoder.yudao.framework.ai.midjourney.api.req.ReRollReq; | 7 | import cn.iocoder.yudao.framework.ai.midjourney.api.req.ReRollReq; |
| 9 | import cn.iocoder.yudao.framework.ai.midjourney.api.res.UploadAttachmentsRes; | 8 | import cn.iocoder.yudao.framework.ai.midjourney.api.res.UploadAttachmentsRes; |
| 10 | -import com.alibaba.fastjson.JSON; | ||
| 11 | -import com.alibaba.fastjson.JSONObject; | 9 | +import cn.iocoder.yudao.framework.ai.midjourney.util.MidjourneyUtil; |
| 10 | +import com.alibaba.fastjson2.JSON; | ||
| 11 | +import com.alibaba.fastjson2.JSONObject; | ||
| 12 | import com.google.common.collect.Lists; | 12 | import com.google.common.collect.Lists; |
| 13 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | import org.springframework.core.io.FileSystemResource; | 14 | import org.springframework.core.io.FileSystemResource; |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MidjourneyMessageListener.java
| @@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyConstants; | @@ -11,7 +11,7 @@ import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyConstants; | ||
| 11 | import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyGennerateStatusEnum; | 11 | import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyGennerateStatusEnum; |
| 12 | import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyMessageTypeEnum; | 12 | import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyMessageTypeEnum; |
| 13 | import cn.iocoder.yudao.framework.ai.midjourney.util.MidjourneyUtil; | 13 | import cn.iocoder.yudao.framework.ai.midjourney.util.MidjourneyUtil; |
| 14 | -import com.alibaba.fastjson.JSON; | 14 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 15 | import lombok.extern.slf4j.Slf4j; | 15 | import lombok.extern.slf4j.Slf4j; |
| 16 | import net.dv8tion.jda.api.utils.data.DataObject; | 16 | import net.dv8tion.jda.api.utils.data.DataObject; |
| 17 | 17 | ||
| @@ -45,13 +45,13 @@ public class MidjourneyMessageListener { | @@ -45,13 +45,13 @@ public class MidjourneyMessageListener { | ||
| 45 | // 转换 components | 45 | // 转换 components |
| 46 | if (!data.getArray(MidjourneyConstants.MSG_COMPONENTS).isEmpty()) { | 46 | if (!data.getArray(MidjourneyConstants.MSG_COMPONENTS).isEmpty()) { |
| 47 | String componentsJson = StrUtil.str(data.getArray(MidjourneyConstants.MSG_COMPONENTS).toJson(), "UTF-8"); | 47 | String componentsJson = StrUtil.str(data.getArray(MidjourneyConstants.MSG_COMPONENTS).toJson(), "UTF-8"); |
| 48 | - List<MidjourneyMessage.ComponentType> components = JSON.parseArray(componentsJson, MidjourneyMessage.ComponentType.class); | 48 | + List<MidjourneyMessage.ComponentType> components = JsonUtils.parseArray(componentsJson, MidjourneyMessage.ComponentType.class); |
| 49 | mjMessage.setComponents(components); | 49 | mjMessage.setComponents(components); |
| 50 | } | 50 | } |
| 51 | // 转换附件 | 51 | // 转换附件 |
| 52 | if (!data.getArray(MidjourneyConstants.MSG_ATTACHMENTS).isEmpty()) { | 52 | if (!data.getArray(MidjourneyConstants.MSG_ATTACHMENTS).isEmpty()) { |
| 53 | String attachmentsJson = StrUtil.str(data.getArray(MidjourneyConstants.MSG_ATTACHMENTS).toJson(), "UTF-8"); | 53 | String attachmentsJson = StrUtil.str(data.getArray(MidjourneyConstants.MSG_ATTACHMENTS).toJson(), "UTF-8"); |
| 54 | - List<MidjourneyMessage.Attachment> attachments = JSON.parseArray(attachmentsJson, MidjourneyMessage.Attachment.class); | 54 | + List<MidjourneyMessage.Attachment> attachments = JsonUtils.parseArray(attachmentsJson, MidjourneyMessage.Attachment.class); |
| 55 | mjMessage.setAttachments(attachments); | 55 | mjMessage.setAttachments(attachments); |
| 56 | } | 56 | } |
| 57 | // 转换状态 | 57 | // 转换状态 |