Commit be8d9c1a5f195fea30c5df5a0e7d9e49ef720e06

Authored by cherishsince
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 // 转换状态