Commit 0f73b61db203b9ab00597eec9265abb4368ff7ea

Authored by cherishsince
1 parent de326117

增加阿里 通义千问

yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
@@ -108,6 +108,12 @@ @@ -108,6 +108,12 @@
108 <groupId>cn.hutool</groupId> 108 <groupId>cn.hutool</groupId>
109 <artifactId>hutool-all</artifactId> 109 <artifactId>hutool-all</artifactId>
110 </dependency> 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 </dependencies> 117 </dependencies>
112 118
113 </project> 119 </project>
114 \ No newline at end of file 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
  1 +package cn.iocoder.yudao.framework.ai.chatqianwen.api;
  2 +
  3 +import com.aliyun.broadscope.bailian.sdk.models.CompletionsResponse;
  4 +
  5 +/**
  6 + * author: fansili
  7 + * time: 2024/3/13 21:07
  8 + */
  9 +public class QianWenChatCompletion extends CompletionsResponse {
  10 +}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenChatCompletionMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.chatqianwen.api;
  2 +
  3 +/**
  4 + * author: fansili
  5 + * time: 2024/3/13 21:07
  6 + */
  7 +public class QianWenChatCompletionMessage {
  8 +}
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
  1 +/**
  2 + * 阿里的 通义千问
  3 + *
  4 + * 链接:https://www.aliyun.com/search?k=%E9%80%9A%E4%B9%89%E5%A4%A7%E6%A8%A1%E5%9E%8B&scene=all
  5 + *
  6 + * author: fansili
  7 + * time: 2024/3/13 21:05
  8 + */
  9 +package cn.iocoder.yudao.framework.ai.chatqianwen;
0 \ No newline at end of file 10 \ No newline at end of file
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,8 +132,6 @@ public class XingHuoApi {
132 } catch (NoSuchAlgorithmException | InvalidKeyException e) { 132 } catch (NoSuchAlgorithmException | InvalidKeyException e) {
133 throw new RuntimeException(e); 133 throw new RuntimeException(e);
134 } 134 }
135 - System.err.println(authUrl);  
136 - System.err.println(JSONUtil.toJsonPrettyStr(request));  
137 // wss 请求的 URI 135 // wss 请求的 URI
138 URI uri = URI.create(authUrl); 136 URI uri = URI.create(authUrl);
139 // 发起 wss 请求并处理响应 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 +}