Commit 94e9ee959017cf8ab45c3449fe2c3da5d5687bb6

Authored by cherishsince
1 parent f41e4371

适配讯飞星火 chatOptions

yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenChatClient.java
@@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.ai.chat.*; @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.ai.chat.*;
6 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType; 6 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType;
7 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; 7 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
9 -import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanOptions;  
10 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; 9 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException;
11 import com.aliyun.broadscope.bailian.sdk.models.*; 10 import com.aliyun.broadscope.bailian.sdk.models.*;
12 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
@@ -19,7 +18,6 @@ import reactor.core.publisher.Flux; @@ -19,7 +18,6 @@ import reactor.core.publisher.Flux;
19 18
20 import java.time.Duration; 19 import java.time.Duration;
21 import java.util.List; 20 import java.util.List;
22 -import java.util.Optional;  
23 import java.util.stream.Collectors; 21 import java.util.stream.Collectors;
24 22
25 /** 23 /**
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatxinghuo/XingHuoApi.java
@@ -39,22 +39,20 @@ public class XingHuoApi { @@ -39,22 +39,20 @@ public class XingHuoApi {
39 private String appKey; 39 private String appKey;
40 private String secretKey; 40 private String secretKey;
41 private WebClient webClient; 41 private WebClient webClient;
42 - private XingHuoChatModel useChatModel;  
43 // 创建 WebSocketClient 实例 42 // 创建 WebSocketClient 实例
44 private ReactorNettyWebSocketClient socketClient = new ReactorNettyWebSocketClient(); 43 private ReactorNettyWebSocketClient socketClient = new ReactorNettyWebSocketClient();
45 44
46 - public XingHuoApi(String appId, String appKey, String secretKey, XingHuoChatModel useChatModel) { 45 + public XingHuoApi(String appId, String appKey, String secretKey) {
47 this.appId = appId; 46 this.appId = appId;
48 this.appKey = appKey; 47 this.appKey = appKey;
49 this.secretKey = secretKey; 48 this.secretKey = secretKey;
50 - this.useChatModel = useChatModel;  
51 -  
52 } 49 }
53 50
54 - public ResponseEntity<XingHuoChatCompletion> chatCompletionEntity(XingHuoChatCompletionRequest request) { 51 + public ResponseEntity<XingHuoChatCompletion> chatCompletionEntity(XingHuoChatCompletionRequest request, XingHuoChatModel xingHuoChatModel) {
55 String authUrl; 52 String authUrl;
56 try { 53 try {
57 - authUrl = getAuthorizationUrl("spark-api.xf-yun.com", useChatModel.getUri()); 54 +// XingHuoChatModel useChatModel;
  55 + authUrl = getAuthorizationUrl("spark-api.xf-yun.com", xingHuoChatModel.getUri());
58 } catch (NoSuchAlgorithmException | InvalidKeyException e) { 56 } catch (NoSuchAlgorithmException | InvalidKeyException e) {
59 throw new RuntimeException(e); 57 throw new RuntimeException(e);
60 } 58 }
@@ -125,10 +123,10 @@ public class XingHuoApi { @@ -125,10 +123,10 @@ public class XingHuoApi {
125 return "wss://" + host + path + "?" + toParams; 123 return "wss://" + host + path + "?" + toParams;
126 } 124 }
127 125
128 - public Flux<XingHuoChatCompletion> chatCompletionStream(XingHuoChatCompletionRequest request) { 126 + public Flux<XingHuoChatCompletion> chatCompletionStream(XingHuoChatCompletionRequest request, XingHuoChatModel xingHuoChatModel) {
129 String authUrl; 127 String authUrl;
130 try { 128 try {
131 - authUrl = getAuthorizationUrl("spark-api.xf-yun.com", useChatModel.getUri()); 129 + authUrl = getAuthorizationUrl("spark-api.xf-yun.com", xingHuoChatModel.getUri());
132 } catch (NoSuchAlgorithmException | InvalidKeyException e) { 130 } catch (NoSuchAlgorithmException | InvalidKeyException e) {
133 throw new RuntimeException(e); 131 throw new RuntimeException(e);
134 } 132 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatxinghuo/XingHuoChatClient.java
1 package cn.iocoder.yudao.framework.ai.chatxinghuo; 1 package cn.iocoder.yudao.framework.ai.chatxinghuo;
2 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; 3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.hutool.core.exceptions.ExceptionUtil;
  5 +import cn.iocoder.yudao.framework.ai.chat.*;
  6 +import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
7 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 7 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
8 import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletion; 8 import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletion;
9 import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletionRequest; 9 import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletionRequest;
10 -import cn.iocoder.yudao.framework.ai.chatxinghuo.exception.XingHuoApiException;  
11 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
12 import org.springframework.http.ResponseEntity; 11 import org.springframework.http.ResponseEntity;
13 import org.springframework.retry.RetryCallback; 12 import org.springframework.retry.RetryCallback;
@@ -31,16 +30,19 @@ public class XingHuoChatClient implements ChatClient, StreamingChatClient { @@ -31,16 +30,19 @@ public class XingHuoChatClient implements ChatClient, StreamingChatClient {
31 30
32 private XingHuoApi xingHuoApi; 31 private XingHuoApi xingHuoApi;
33 32
  33 + private XingHuoOptions xingHuoOptions;
  34 +
34 public final RetryTemplate retryTemplate = RetryTemplate.builder() 35 public final RetryTemplate retryTemplate = RetryTemplate.builder()
35 // 最大重试次数 10 36 // 最大重试次数 10
36 - .maxAttempts(10)  
37 - .retryOn(XingHuoApiException.class) 37 + .maxAttempts(3)
  38 + .retryOn(ChatException.class)
38 // 最大重试5次,第一次间隔3000ms,第二次3000ms * 2,第三次3000ms * 3,以此类推,最大间隔3 * 60000ms 39 // 最大重试5次,第一次间隔3000ms,第二次3000ms * 2,第三次3000ms * 3,以此类推,最大间隔3 * 60000ms
39 .exponentialBackoff(Duration.ofMillis(3000), 2, Duration.ofMillis(3 * 60000)) 40 .exponentialBackoff(Duration.ofMillis(3000), 2, Duration.ofMillis(3 * 60000))
40 .withListener(new RetryListener() { 41 .withListener(new RetryListener() {
41 @Override 42 @Override
42 public <T extends Object, E extends Throwable> void onError(RetryContext context, 43 public <T extends Object, E extends Throwable> void onError(RetryContext context,
43 RetryCallback<T, E> callback, Throwable throwable) { 44 RetryCallback<T, E> callback, Throwable throwable) {
  45 + System.err.println("正在重试... " + ExceptionUtil.getMessage(throwable));
44 log.warn("重试异常:" + context.getRetryCount(), throwable); 46 log.warn("重试异常:" + context.getRetryCount(), throwable);
45 } 47 }
46 48
@@ -52,26 +54,67 @@ public class XingHuoChatClient implements ChatClient, StreamingChatClient { @@ -52,26 +54,67 @@ public class XingHuoChatClient implements ChatClient, StreamingChatClient {
52 this.xingHuoApi = xingHuoApi; 54 this.xingHuoApi = xingHuoApi;
53 } 55 }
54 56
  57 + public XingHuoChatClient(XingHuoApi xingHuoApi, XingHuoOptions xingHuoOptions) {
  58 + this.xingHuoApi = xingHuoApi;
  59 + this.xingHuoOptions = xingHuoOptions;
  60 + }
  61 +
55 @Override 62 @Override
56 public ChatResponse call(Prompt prompt) { 63 public ChatResponse call(Prompt prompt) {
57 64
58 return this.retryTemplate.execute(ctx -> { 65 return this.retryTemplate.execute(ctx -> {
59 // ctx 会有重试的信息 66 // ctx 会有重试的信息
  67 + // 获取 chatOptions 属性
  68 + XingHuoOptions chatOptions = this.getChatOptions(prompt);
60 // 创建 request 请求,stream模式需要供应商支持 69 // 创建 request 请求,stream模式需要供应商支持
61 - XingHuoChatCompletionRequest request = this.createRequest(prompt, false); 70 + XingHuoChatCompletionRequest request = this.createRequest(prompt, chatOptions);
62 // 调用 callWithFunctionSupport 发送请求 71 // 调用 callWithFunctionSupport 发送请求
63 - ResponseEntity<XingHuoChatCompletion> response = xingHuoApi.chatCompletionEntity(request); 72 + ResponseEntity<XingHuoChatCompletion> response = xingHuoApi.chatCompletionEntity(request, chatOptions.getDomain());
64 // 获取结果封装 ChatResponse 73 // 获取结果封装 ChatResponse
65 return new ChatResponse(List.of(new Generation(response.getBody().getPayload().getChoices().getText().get(0).getContent()))); 74 return new ChatResponse(List.of(new Generation(response.getBody().getPayload().getChoices().getText().get(0).getContent())));
66 }); 75 });
67 } 76 }
68 77
69 - private XingHuoChatCompletionRequest createRequest(Prompt prompt, boolean b) { 78 + @Override
  79 + public Flux<ChatResponse> stream(Prompt prompt) {
  80 + // 获取 chatOptions 属性
  81 + XingHuoOptions chatOptions = this.getChatOptions(prompt);
  82 + // 创建 request 请求,stream模式需要供应商支持
  83 + XingHuoChatCompletionRequest request = this.createRequest(prompt, chatOptions);
  84 + // 发送请求
  85 + Flux<XingHuoChatCompletion> response = this.xingHuoApi.chatCompletionStream(request, chatOptions.getDomain());
  86 + return response.map(res -> {
  87 + String content = res.getPayload().getChoices().getText().stream()
  88 + .map(item -> item.getContent()).collect(Collectors.joining());
  89 + return new ChatResponse(List.of(new Generation(content)));
  90 + });
  91 + }
  92 +
  93 + private XingHuoOptions getChatOptions(Prompt prompt) {
  94 + // 两个都为null 则没有配置文件
  95 + if (xingHuoOptions == null && prompt.getOptions() == null) {
  96 + throw new ChatException("ChatOptions 未配置参数!");
  97 + }
  98 + // 优先使用 Prompt 里面的 ChatOptions
  99 + ChatOptions options = xingHuoOptions;
  100 + if (prompt.getOptions() != null) {
  101 + options = (ChatOptions) prompt.getOptions();
  102 + }
  103 + // Prompt 里面是一个 ChatOptions,用户可以随意传入,这里做一下判断
  104 + if (!(options instanceof XingHuoOptions)) {
  105 + throw new ChatException("Prompt 传入的不是 XingHuoOptions!");
  106 + }
  107 + return (XingHuoOptions) options;
  108 + }
  109 +
  110 + private XingHuoChatCompletionRequest createRequest(Prompt prompt, XingHuoOptions xingHuoOptions) {
70 // 创建 header 111 // 创建 header
71 XingHuoChatCompletionRequest.Header header = new XingHuoChatCompletionRequest.Header().setApp_id(xingHuoApi.getAppId()); 112 XingHuoChatCompletionRequest.Header header = new XingHuoChatCompletionRequest.Header().setApp_id(xingHuoApi.getAppId());
72 // 创建 params 113 // 创建 params
73 - XingHuoChatCompletionRequest.Parameter parameter = new XingHuoChatCompletionRequest.Parameter()  
74 - .setChat(new XingHuoChatCompletionRequest.Parameter.Chat().setDomain(xingHuoApi.getUseChatModel().getValue())); 114 + XingHuoChatCompletionRequest.Parameter.Chat chatParameter = new XingHuoChatCompletionRequest.Parameter.Chat();
  115 + BeanUtil.copyProperties(xingHuoOptions, chatParameter);
  116 + chatParameter.setDomain(xingHuoOptions.getDomain().getValue());
  117 + XingHuoChatCompletionRequest.Parameter parameter = new XingHuoChatCompletionRequest.Parameter().setChat(chatParameter);
75 // 创建 payload text 信息 118 // 创建 payload text 信息
76 XingHuoChatCompletionRequest.Payload.Message.Text text = new XingHuoChatCompletionRequest.Payload.Message.Text(); 119 XingHuoChatCompletionRequest.Payload.Message.Text text = new XingHuoChatCompletionRequest.Payload.Message.Text();
77 text.setRole(XingHuoChatCompletionRequest.Payload.Message.Text.Role.USER.getName()); 120 text.setRole(XingHuoChatCompletionRequest.Payload.Message.Text.Role.USER.getName());
@@ -85,17 +128,4 @@ public class XingHuoChatClient implements ChatClient, StreamingChatClient { @@ -85,17 +128,4 @@ public class XingHuoChatClient implements ChatClient, StreamingChatClient {
85 .setParameter(parameter) 128 .setParameter(parameter)
86 .setPayload(payload); 129 .setPayload(payload);
87 } 130 }
88 -  
89 - @Override  
90 - public Flux<ChatResponse> stream(Prompt prompt) {  
91 - // 创建 request 请求,stream模式需要供应商支持  
92 - XingHuoChatCompletionRequest request = this.createRequest(prompt, false);  
93 - // 发送请求  
94 - Flux<XingHuoChatCompletion> response = this.xingHuoApi.chatCompletionStream(request);  
95 - return response.map(res -> {  
96 - String content = res.getPayload().getChoices().getText().stream()  
97 - .map(item -> item.getContent()).collect(Collectors.joining());  
98 - return new ChatResponse(List.of(new Generation(content)));  
99 - });  
100 - }  
101 } 131 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatxinghuo/XingHuoOptions.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.chatxinghuo;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
  4 +import lombok.Data;
  5 +import lombok.experimental.Accessors;
  6 +
  7 +/**
  8 + * 讯飞星火
  9 + *
  10 + * author: fansili
  11 + * time: 2024/3/16 20:29
  12 + */
  13 +@Data
  14 +@Accessors(chain = true)
  15 +public class XingHuoOptions implements ChatOptions {
  16 +
  17 + /**
  18 + * https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
  19 + *
  20 + * 指定访问的领域:
  21 + * general指向V1.5版本;
  22 + * generalv2指向V2版本;
  23 + * generalv3指向V3版本;
  24 + * generalv3.5指向V3.5版本;
  25 + * 注意:不同的取值对应的url也不一样!
  26 + */
  27 + private XingHuoChatModel domain = XingHuoChatModel.XING_HUO_3_5;
  28 + /**
  29 + * 取值范围 (0,1] ,默认值0.5
  30 + */
  31 + private Float temperature;
  32 + /**
  33 + * V1.5取值为[1,4096]
  34 + * V2.0、V3.0和V3.5取值为[1,8192],默认为2048。
  35 + */
  36 + private Integer max_tokens;
  37 + /**
  38 + * 取值为[1,6],默认为4
  39 + */
  40 + private Integer top_k;
  41 + /**
  42 + * 需要保障用户下的唯一性,用于关联用户会话
  43 + */
  44 + private String chat_id;
  45 +
  46 +
  47 + @Override
  48 + public Float getTemperature() {
  49 + return null;
  50 + }
  51 +
  52 + @Override
  53 + public void setTemperature(Float temperature) {
  54 +
  55 + }
  56 +
  57 + @Override
  58 + public Float getTopP() {
  59 + return null;
  60 + }
  61 +
  62 + @Override
  63 + public void setTopP(Float topP) {
  64 +
  65 + }
  66 +
  67 + @Override
  68 + public Integer getTopK() {
  69 + return null;
  70 + }
  71 +
  72 + @Override
  73 + public void setTopK(Integer topK) {
  74 +
  75 + }
  76 +}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatxinghuo/api/XingHuoChatCompletionRequest.java
@@ -45,9 +45,24 @@ public class XingHuoChatCompletionRequest { @@ -45,9 +45,24 @@ public class XingHuoChatCompletionRequest {
45 * generalv3.5指向V3.5版本; 45 * generalv3.5指向V3.5版本;
46 * 注意:不同的取值对应的url也不一样! 46 * 注意:不同的取值对应的url也不一样!
47 */ 47 */
48 - private String domain = "general";  
49 - private Double temperature = 0.5;  
50 - private Integer max_tokens = 2048; 48 + private String domain = "generalv3.5";
  49 + /**
  50 + * 取值范围 (0,1] ,默认值0.5
  51 + */
  52 + private Float temperature;
  53 + /**
  54 + * V1.5取值为[1,4096]
  55 + * V2.0、V3.0和V3.5取值为[1,8192],默认为2048。
  56 + */
  57 + private Integer max_tokens;
  58 + /**
  59 + * 取值为[1,6],默认为4
  60 + */
  61 + private Integer top_k;
  62 + /**
  63 + * 需要保障用户下的唯一性,用于关联用户会话
  64 + */
  65 + private String chat_id;
51 } 66 }
52 } 67 }
53 68
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatxinghuo/exception/XingHuoApiException.java deleted
1 -package cn.iocoder.yudao.framework.ai.chatxinghuo.exception;  
2 -  
3 -/**  
4 - * 讯飞星火 exception  
5 - *  
6 - * author: fansili  
7 - * time: 2024/3/11 10:22  
8 - */  
9 -public class XingHuoApiException extends RuntimeException {  
10 -  
11 - public XingHuoApiException(String message) {  
12 - super(message);  
13 - }  
14 -}  
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/QianWenChatClientTests.java
@@ -23,9 +23,9 @@ public class QianWenChatClientTests { @@ -23,9 +23,9 @@ public class QianWenChatClientTests {
23 @Before 23 @Before
24 public void setup() { 24 public void setup() {
25 QianWenApi qianWenApi = new QianWenApi( 25 QianWenApi qianWenApi = new QianWenApi(
26 - "LTAI5tNTVhXW4fLKUjMrr98z",  
27 - "ZJ0JQeyjzxxm5CfeTV6k1wNE9UsvZP",  
28 - "f0c1088824594f589c8f10567ccd929f_p_efm", 26 + "",
  27 + "",
  28 + "",
29 null 29 null
30 ); 30 );
31 qianWenChatClient = new QianWenChatClient( 31 qianWenChatClient = new QianWenChatClient(
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatClientTests.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
4 import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoApi; 4 import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoApi;
5 import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatClient; 5 import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatClient;
6 import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatModel; 6 import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatModel;
  7 +import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoOptions;
7 import org.junit.Before; 8 import org.junit.Before;
8 import org.junit.Test; 9 import org.junit.Test;
9 import reactor.core.publisher.Flux; 10 import reactor.core.publisher.Flux;
@@ -28,9 +29,9 @@ public class XingHuoChatClientTests { @@ -28,9 +29,9 @@ public class XingHuoChatClientTests {
28 new XingHuoApi( 29 new XingHuoApi(
29 "13c8cca6", 30 "13c8cca6",
30 "cb6415c19d6162cda07b47316fcb0416", 31 "cb6415c19d6162cda07b47316fcb0416",
31 - "Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh",  
32 - XingHuoChatModel.XING_HUO_3_5  
33 - ) 32 + "Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh"
  33 + ),
  34 + new XingHuoOptions().setDomain(XingHuoChatModel.XING_HUO_3_5)
34 ); 35 );
35 } 36 }
36 37