Commit 0f73b61db203b9ab00597eec9265abb4368ff7ea
1 parent
de326117
增加阿里 通义千问
Showing
9 changed files
with
308 additions
and
2 deletions
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
| ... | ... | @@ -108,6 +108,12 @@ |
| 108 | 108 | <groupId>cn.hutool</groupId> |
| 109 | 109 | <artifactId>hutool-all</artifactId> |
| 110 | 110 | </dependency> |
| 111 | + <!-- 阿里云 通义千问 --> | |
| 112 | + <dependency> | |
| 113 | + <groupId>com.aliyun</groupId> | |
| 114 | + <artifactId>broadscope-bailian-sdk-java</artifactId> | |
| 115 | + <version>1.3.0</version> | |
| 116 | + </dependency> | |
| 111 | 117 | </dependencies> |
| 112 | 118 | |
| 113 | 119 | </project> |
| 114 | 120 | \ No newline at end of file | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenApi.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.ai.chatqianwen; | |
| 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.ChatRequestMessage; | |
| 6 | +import com.aliyun.broadscope.bailian.sdk.models.CompletionsRequest; | |
| 7 | +import com.aliyun.broadscope.bailian.sdk.models.CompletionsResponse; | |
| 8 | +import lombok.Getter; | |
| 9 | +import org.springframework.http.HttpStatusCode; | |
| 10 | +import org.springframework.http.ResponseEntity; | |
| 11 | +import reactor.core.publisher.Flux; | |
| 12 | + | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * 阿里 通义千问 | |
| 17 | + * | |
| 18 | + * https://www.aliyun.com/search?k=%E9%80%9A%E4%B9%89%E5%A4%A7%E6%A8%A1%E5%9E%8B&scene=all | |
| 19 | + * | |
| 20 | + * author: fansili | |
| 21 | + * time: 2024/3/13 21:09 | |
| 22 | + */ | |
| 23 | +@Getter | |
| 24 | +public class QianWenApi { | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * accessKeyId、accessKeySecret、agentKey、appId 获取方式如下链接 | |
| 28 | + * https://help.aliyun.com/document_detail/2587494.html?spm=a2c4g.2587492.0.0.53f33c566sXskp | |
| 29 | + */ | |
| 30 | + private String accessKeyId; | |
| 31 | + private String accessKeySecret; | |
| 32 | + private String agentKey; | |
| 33 | + private String appId; | |
| 34 | + private String endpoint = "bailian.cn-beijing.aliyuncs.com"; | |
| 35 | + private String token; | |
| 36 | + private ApplicationClient client; | |
| 37 | + | |
| 38 | + public QianWenApi(String accessKeyId, String accessKeySecret, String agentKey, String appId, String endpoint) { | |
| 39 | + this.accessKeyId = accessKeyId; | |
| 40 | + this.accessKeySecret = accessKeySecret; | |
| 41 | + this.agentKey = agentKey; | |
| 42 | + this.appId = appId; | |
| 43 | + | |
| 44 | + if (endpoint != null) { | |
| 45 | + this.endpoint = endpoint; | |
| 46 | + } | |
| 47 | + | |
| 48 | + // 获取token | |
| 49 | + AccessTokenClient accessTokenClient = new AccessTokenClient(accessKeyId, accessKeySecret, agentKey); | |
| 50 | + token = accessTokenClient.getToken(); | |
| 51 | + // 构建client | |
| 52 | + client = ApplicationClient.builder() | |
| 53 | + .token(token) | |
| 54 | + .build(); | |
| 55 | + } | |
| 56 | + | |
| 57 | + public ResponseEntity<CompletionsResponse> chatCompletionEntity(ChatRequestMessage message) { | |
| 58 | + // 创建request | |
| 59 | + CompletionsRequest request = new CompletionsRequest() | |
| 60 | + .setAppId(appId) | |
| 61 | + .setMessages(List.of(message)) | |
| 62 | + .setParameters(new CompletionsRequest.Parameter().setResultFormat("message")); | |
| 63 | + // | |
| 64 | + CompletionsResponse response = client.completions(request); | |
| 65 | + int httpCode = 200; | |
| 66 | + if (!response.isSuccess()) { | |
| 67 | + System.out.printf("failed to create completion, requestId: %s, code: %s, message: %s\n", | |
| 68 | + response.getRequestId(), response.getCode(), response.getMessage()); | |
| 69 | + httpCode = 500; | |
| 70 | + } | |
| 71 | + return new ResponseEntity<>(response, HttpStatusCode.valueOf(httpCode)); | |
| 72 | + } | |
| 73 | + | |
| 74 | + public Flux<CompletionsResponse> chatCompletionStream(ChatRequestMessage message) { | |
| 75 | + return client.streamCompletions( | |
| 76 | + new CompletionsRequest() | |
| 77 | + .setAppId(appId) | |
| 78 | + .setMessages(List.of(message)) | |
| 79 | + .setParameters(new CompletionsRequest.Parameter().setIncrementalOutput(true)) | |
| 80 | + ); | |
| 81 | + } | |
| 82 | +} | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenChatClient.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.ai.chatqianwen; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.ai.chat.ChatClient; | |
| 4 | +import cn.iocoder.yudao.framework.ai.chat.ChatResponse; | |
| 5 | +import cn.iocoder.yudao.framework.ai.chat.Generation; | |
| 6 | +import cn.iocoder.yudao.framework.ai.chat.StreamingChatClient; | |
| 7 | +import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; | |
| 8 | +import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenChatCompletionMessage; | |
| 9 | +import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenChatCompletionRequest; | |
| 10 | +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion; | |
| 11 | +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; | |
| 12 | +import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; | |
| 13 | +import cn.iocoder.yudao.framework.ai.model.function.AbstractFunctionCallSupport; | |
| 14 | +import cn.iocoder.yudao.framework.ai.model.function.FunctionCallbackContext; | |
| 15 | +import com.aliyun.broadscope.bailian.sdk.models.ChatRequestMessage; | |
| 16 | +import com.aliyun.broadscope.bailian.sdk.models.ChatUserMessage; | |
| 17 | +import com.aliyun.broadscope.bailian.sdk.models.CompletionsResponse; | |
| 18 | +import lombok.extern.slf4j.Slf4j; | |
| 19 | +import org.springframework.http.ResponseEntity; | |
| 20 | +import org.springframework.retry.RetryCallback; | |
| 21 | +import org.springframework.retry.RetryContext; | |
| 22 | +import org.springframework.retry.RetryListener; | |
| 23 | +import org.springframework.retry.support.RetryTemplate; | |
| 24 | +import reactor.core.publisher.Flux; | |
| 25 | + | |
| 26 | +import java.time.Duration; | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.stream.Collectors; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + * 阿里 通义千问 client | |
| 32 | + * | |
| 33 | + * 文档地址:https://help.aliyun.com/document_detail/2587494.html?spm=a2c4g.2587492.0.0.53f33c566sXskp | |
| 34 | + * | |
| 35 | + * author: fansili | |
| 36 | + * time: 2024/3/13 21:06 | |
| 37 | + */ | |
| 38 | +@Slf4j | |
| 39 | +public class QianWenChatClient extends AbstractFunctionCallSupport<QianWenChatCompletionMessage, ChatRequestMessage, ResponseEntity<CompletionsResponse>> | |
| 40 | + implements ChatClient, StreamingChatClient { | |
| 41 | + | |
| 42 | + private QianWenApi qianWenApi; | |
| 43 | + | |
| 44 | + public QianWenChatClient(QianWenApi qianWenApi) { | |
| 45 | + super(null); | |
| 46 | + this.qianWenApi = qianWenApi; | |
| 47 | + } | |
| 48 | + | |
| 49 | + public final RetryTemplate retryTemplate = RetryTemplate.builder() | |
| 50 | + // 最大重试次数 10 | |
| 51 | + .maxAttempts(10) | |
| 52 | + .retryOn(YiYanApiException.class) | |
| 53 | + // 最大重试5次,第一次间隔3000ms,第二次3000ms * 2,第三次3000ms * 3,以此类推,最大间隔3 * 60000ms | |
| 54 | + .exponentialBackoff(Duration.ofMillis(3000), 2, Duration.ofMillis(3 * 60000)) | |
| 55 | + .withListener(new RetryListener() { | |
| 56 | + @Override | |
| 57 | + public <T extends Object, E extends Throwable> void onError(RetryContext context, | |
| 58 | + RetryCallback<T, E> callback, Throwable throwable) { | |
| 59 | + log.warn("重试异常:" + context.getRetryCount(), throwable); | |
| 60 | + }; | |
| 61 | + }) | |
| 62 | + .build(); | |
| 63 | + | |
| 64 | + public QianWenChatClient(FunctionCallbackContext functionCallbackContext) { | |
| 65 | + super(functionCallbackContext); | |
| 66 | + } | |
| 67 | + | |
| 68 | + @Override | |
| 69 | + public ChatResponse call(Prompt prompt) { | |
| 70 | + return this.retryTemplate.execute(ctx -> { | |
| 71 | + // ctx 会有重试的信息 | |
| 72 | + // 创建 request 请求,stream模式需要供应商支持 | |
| 73 | + ChatRequestMessage request = this.createRequest(prompt, false); | |
| 74 | + // 调用 callWithFunctionSupport 发送请求 | |
| 75 | + ResponseEntity<CompletionsResponse> responseEntity = this.callWithFunctionSupport(request); | |
| 76 | + // 获取结果封装 chatCompletion | |
| 77 | + CompletionsResponse response = responseEntity.getBody(); | |
| 78 | + if (!response.isSuccess()) { | |
| 79 | + return new ChatResponse(List.of(new Generation(String.format("failed to create completion, requestId: %s, code: %s, message: %s\n", | |
| 80 | + response.getRequestId(), response.getCode(), response.getMessage())))); | |
| 81 | + } | |
| 82 | + List<Generation> generations = response.getData().getChoices().stream() | |
| 83 | + .map(item -> new Generation(item.getMessage().getContent())).collect(Collectors.toList()); | |
| 84 | + return new ChatResponse(generations); | |
| 85 | + }); | |
| 86 | + } | |
| 87 | + | |
| 88 | + private ChatRequestMessage createRequest(Prompt prompt, boolean b) { | |
| 89 | + return new ChatUserMessage(prompt.getContents()); | |
| 90 | + } | |
| 91 | + | |
| 92 | + @Override | |
| 93 | + public Flux<ChatResponse> stream(Prompt prompt) { | |
| 94 | + // ctx 会有重试的信息 | |
| 95 | + // 创建 request 请求,stream模式需要供应商支持 | |
| 96 | + ChatRequestMessage request = this.createRequest(prompt, true); | |
| 97 | + // 调用 callWithFunctionSupport 发送请求 | |
| 98 | + Flux<CompletionsResponse> response = this.qianWenApi.chatCompletionStream(request); | |
| 99 | + return response.map(res -> { | |
| 100 | + return new ChatResponse(List.of(new Generation(res.getData().getText()))); | |
| 101 | + }); | |
| 102 | + } | |
| 103 | + | |
| 104 | + @Override | |
| 105 | + protected QianWenChatCompletionRequest doCreateToolResponseRequest(ChatRequestMessage previousRequest, QianWenChatCompletionMessage responseMessage, List<QianWenChatCompletionMessage> conversationHistory) { | |
| 106 | + return null; | |
| 107 | + } | |
| 108 | + | |
| 109 | + @Override | |
| 110 | + protected List<QianWenChatCompletionMessage> doGetUserMessages(ChatRequestMessage request) { | |
| 111 | + return null; | |
| 112 | + } | |
| 113 | + | |
| 114 | + @Override | |
| 115 | + protected QianWenChatCompletionMessage doGetToolResponseMessage(ResponseEntity<CompletionsResponse> response) { | |
| 116 | + return null; | |
| 117 | + } | |
| 118 | + | |
| 119 | + @Override | |
| 120 | + protected ResponseEntity<CompletionsResponse> doChatCompletion(ChatRequestMessage request) { | |
| 121 | + return qianWenApi.chatCompletionEntity(request); | |
| 122 | + } | |
| 123 | + | |
| 124 | + @Override | |
| 125 | + protected boolean isToolFunctionCall(ResponseEntity<CompletionsResponse> response) { | |
| 126 | + return false; | |
| 127 | + } | |
| 128 | +} | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenChatCompletion.java
0 → 100644
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenChatCompletionMessage.java
0 → 100644
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenChatCompletionRequest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.ai.chatqianwen.api; | |
| 2 | + | |
| 3 | +import com.aliyun.broadscope.bailian.sdk.models.ChatRequestMessage; | |
| 4 | +import com.aliyun.broadscope.bailian.sdk.models.ChatUserMessage; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * author: fansili | |
| 8 | + * time: 2024/3/13 21:07 | |
| 9 | + */ | |
| 10 | +public class QianWenChatCompletionRequest extends ChatRequestMessage { | |
| 11 | + | |
| 12 | +} | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/package-info.java
0 → 100644
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatxinghuo/XingHuoApi.java
| ... | ... | @@ -132,8 +132,6 @@ public class XingHuoApi { |
| 132 | 132 | } catch (NoSuchAlgorithmException | InvalidKeyException e) { |
| 133 | 133 | throw new RuntimeException(e); |
| 134 | 134 | } |
| 135 | - System.err.println(authUrl); | |
| 136 | - System.err.println(JSONUtil.toJsonPrettyStr(request)); | |
| 137 | 135 | // wss 请求的 URI |
| 138 | 136 | URI uri = URI.create(authUrl); |
| 139 | 137 | // 发起 wss 请求并处理响应 | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/QianWenChatClientTests.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.ai.chat; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; | |
| 4 | +import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenApi; | |
| 5 | +import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatClient; | |
| 6 | +import org.junit.Before; | |
| 7 | +import org.junit.Test; | |
| 8 | +import reactor.core.publisher.Flux; | |
| 9 | + | |
| 10 | +import java.util.Scanner; | |
| 11 | +import java.util.function.Consumer; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * author: fansili | |
| 15 | + * time: 2024/3/13 21:37 | |
| 16 | + */ | |
| 17 | +public class QianWenChatClientTests { | |
| 18 | + | |
| 19 | + private QianWenChatClient qianWenChatClient; | |
| 20 | + | |
| 21 | + @Before | |
| 22 | + public void setup() { | |
| 23 | + QianWenApi qianWenApi = new QianWenApi( | |
| 24 | + "", | |
| 25 | + "", | |
| 26 | + "", | |
| 27 | + "", | |
| 28 | + null | |
| 29 | + ); | |
| 30 | + qianWenChatClient = new QianWenChatClient(qianWenApi); | |
| 31 | + } | |
| 32 | + | |
| 33 | + @Test | |
| 34 | + public void callTest() { | |
| 35 | + ChatResponse call = qianWenChatClient.call(new Prompt("Java语言怎么样?")); | |
| 36 | + System.err.println(call.getResult()); | |
| 37 | + } | |
| 38 | + | |
| 39 | + @Test | |
| 40 | + public void streamTest() { | |
| 41 | + Flux<ChatResponse> flux = qianWenChatClient.stream(new Prompt("Java语言怎么样?")); | |
| 42 | + flux.subscribe(new Consumer<ChatResponse>() { | |
| 43 | + @Override | |
| 44 | + public void accept(ChatResponse chatResponse) { | |
| 45 | + System.err.print(chatResponse.getResult().getOutput().getContent()); | |
| 46 | + } | |
| 47 | + }); | |
| 48 | + | |
| 49 | + // 阻止退出 | |
| 50 | + Scanner scanner = new Scanner(System.in); | |
| 51 | + scanner.nextLine(); | |
| 52 | + } | |
| 53 | +} | ... | ... |