Commit f41e43713c9ca32bf72420f6c135b5bb7ea1e01e

Authored by cherishsince
1 parent 1fada268

百度 文心一言 适配chatOptions

yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatClient.java
1 package cn.iocoder.yudao.framework.ai.chatyiyan; 1 package cn.iocoder.yudao.framework.ai.chatyiyan;
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;  
7 -import cn.iocoder.yudao.framework.ai.chat.messages.Message; 3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.iocoder.yudao.framework.ai.chat.*;
  5 +import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 6 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
9 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion; 7 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion;
10 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; 8 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest;
@@ -18,7 +16,6 @@ import org.springframework.retry.support.RetryTemplate; @@ -18,7 +16,6 @@ import org.springframework.retry.support.RetryTemplate;
18 import reactor.core.publisher.Flux; 16 import reactor.core.publisher.Flux;
19 17
20 import java.time.Duration; 18 import java.time.Duration;
21 -import java.util.ArrayList;  
22 import java.util.List; 19 import java.util.List;
23 20
24 /** 21 /**
@@ -32,10 +29,17 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { @@ -32,10 +29,17 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
32 29
33 private YiYanApi yiYanApi; 30 private YiYanApi yiYanApi;
34 31
  32 + private YiYanOptions yiYanOptions;
  33 +
35 public YiYanChatClient(YiYanApi yiYanApi) { 34 public YiYanChatClient(YiYanApi yiYanApi) {
36 this.yiYanApi = yiYanApi; 35 this.yiYanApi = yiYanApi;
37 } 36 }
38 37
  38 + public YiYanChatClient(YiYanApi yiYanApi, YiYanOptions yiYanOptions) {
  39 + this.yiYanApi = yiYanApi;
  40 + this.yiYanOptions = yiYanOptions;
  41 + }
  42 +
39 public final RetryTemplate retryTemplate = RetryTemplate.builder() 43 public final RetryTemplate retryTemplate = RetryTemplate.builder()
40 // 最大重试次数 10 44 // 最大重试次数 10
41 .maxAttempts(10) 45 .maxAttempts(10)
@@ -70,20 +74,6 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { @@ -70,20 +74,6 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
70 }); 74 });
71 } 75 }
72 76
73 - private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) {  
74 - List<YiYanChatCompletionRequest.Message> messages = new ArrayList<>();  
75 - List<Message> instructions = prompt.getInstructions();  
76 - for (Message instruction : instructions) {  
77 - YiYanChatCompletionRequest.Message message = new YiYanChatCompletionRequest.Message();  
78 - message.setContent(instruction.getContent());  
79 - message.setRole(instruction.getMessageType().getValue());  
80 - messages.add(message);  
81 - }  
82 - YiYanChatCompletionRequest request = new YiYanChatCompletionRequest(messages);  
83 - request.setStream(stream);  
84 - return request;  
85 - }  
86 -  
87 @Override 77 @Override
88 public Flux<ChatResponse> stream(Prompt prompt) { 78 public Flux<ChatResponse> stream(Prompt prompt) {
89 // ctx 会有重试的信息 79 // ctx 会有重试的信息
@@ -93,4 +83,35 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { @@ -93,4 +83,35 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
93 Flux<YiYanChatCompletion> response = this.yiYanApi.chatCompletionStream(request); 83 Flux<YiYanChatCompletion> response = this.yiYanApi.chatCompletionStream(request);
94 return response.map(res -> new ChatResponse(List.of(new Generation(res.getResult())))); 84 return response.map(res -> new ChatResponse(List.of(new Generation(res.getResult()))));
95 } 85 }
  86 +
  87 + private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
  88 + // 两个都为null 则没有配置文件
  89 + if (yiYanOptions == null && prompt.getOptions() == null) {
  90 + throw new ChatException("ChatOptions 未配置参数!");
  91 + }
  92 + // 优先使用 Prompt 里面的 ChatOptions
  93 + ChatOptions options = yiYanOptions;
  94 + if (prompt.getOptions() != null) {
  95 + options = (ChatOptions) prompt.getOptions();
  96 + }
  97 + // Prompt 里面是一个 ChatOptions,用户可以随意传入,这里做一下判断
  98 + if (!(options instanceof YiYanOptions)) {
  99 + throw new ChatException("Prompt 传入的不是 YiYanOptions!");
  100 + }
  101 + // 转换 YiYanOptions
  102 + YiYanOptions qianWenOptions = (YiYanOptions) options;
  103 + // 创建 request
  104 + List<YiYanChatCompletionRequest.Message> messageList = prompt.getInstructions().stream().map(
  105 + msg -> new YiYanChatCompletionRequest.Message()
  106 + .setRole(msg.getMessageType().getValue())
  107 + .setContent(msg.getContent())
  108 + ).toList();
  109 + YiYanChatCompletionRequest request = new YiYanChatCompletionRequest(messageList);
  110 + // 复制 qianWenOptions 属性取 request(这里 options 属性和 request 基本保持一致)
  111 + // top: 由于遵循 spring-ai规范,支持在构建client的时候传入默认的 chatOptions
  112 + BeanUtil.copyProperties(qianWenOptions, request);
  113 + // 设置 stream
  114 + request.setStream(stream);
  115 + return request;
  116 + }
96 } 117 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanOptions.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.chatyiyan;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
  4 +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest;
  5 +import com.fasterxml.jackson.annotation.JsonProperty;
  6 +import lombok.Data;
  7 +import lombok.experimental.Accessors;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * 百度 问心一言
  13 + *
  14 + * 文档地址:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/clntwmv7t
  15 + *
  16 + * author: fansili
  17 + * time: 2024/3/16 19:33
  18 + */
  19 +@Data
  20 +@Accessors(chain = true)
  21 +public class YiYanOptions implements ChatOptions {
  22 +
  23 + /**
  24 + * 一个可触发函数的描述列表,说明:
  25 + * (1)支持的function数量无限制
  26 + * (2)长度限制,最后一个message的content长度(即此轮对话的问题)、functions和system字段总内容不能超过20480 个字符,且不能超过5120 tokens
  27 + * 必填:否
  28 + */
  29 + private List<YiYanChatCompletionRequest.Function> functions;
  30 + /**
  31 + * 说明:
  32 + * (1)较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定
  33 + * (2)默认0.8,范围 (0, 1.0],不能为0
  34 + * 必填:否
  35 + */
  36 + private Float temperature;
  37 + /**
  38 + * 说明:
  39 + * (1)影响输出文本的多样性,取值越大,生成文本的多样性越强
  40 + * (2)默认0.8,取值范围 [0, 1.0]
  41 + * 必填:否
  42 + */
  43 + private Float top_p;
  44 + /**
  45 + * 通过对已生成的token增加惩罚,减少重复生成的现象。说明:
  46 + * (1)值越大表示惩罚越大
  47 + * (2)默认1.0,取值范围:[1.0, 2.0]
  48 + *
  49 + * 必填:否
  50 + */
  51 + private Float penalty_score;
  52 + /**
  53 + * 是否以流式接口的形式返回数据,默认false
  54 + * 必填:否
  55 + */
  56 + private Boolean stream;
  57 + /**
  58 + * 模型人设,主要用于人设设定,例如,你是xxx公司制作的AI助手,说明:
  59 + * (1)长度限制,最后一个message的content长度(即此轮对话的问题)、functions和system字段总内容不能超过20480 个字符,且不能超过5120 tokens
  60 + * (2)如果同时使用system和functions,可能暂无法保证使用效果,持续进行优化
  61 + * 必填:否
  62 + */
  63 + private String system;
  64 + /**
  65 + * 生成停止标识,当模型生成结果以stop中某个元素结尾时,停止文本生成。说明:
  66 + * (1)每个元素长度不超过20字符
  67 + * (2)最多4个元素
  68 + * 必填:否
  69 + */
  70 + private List<String> stop;
  71 + /**
  72 + * 是否强制关闭实时搜索功能,默认false,表示不关闭
  73 + * 必填:否
  74 + */
  75 + private Boolean disable_search;
  76 + /**
  77 + * 是否开启上角标返回,说明:
  78 + * (1)开启后,有概率触发搜索溯源信息search_info,search_info内容见响应参数介绍
  79 + * (2)默认false,不开启
  80 + * 必填:否
  81 + */
  82 + private Boolean enable_citation;
  83 + /**
  84 + * 指定模型最大输出token数,范围[2, 2048]
  85 + * 必填:否
  86 + */
  87 + private Integer max_output_tokens;
  88 + /**
  89 + * 指定响应内容的格式,说明:
  90 + * (1)可选值:
  91 + * · json_object:以json格式返回,可能出现不满足效果情况
  92 + * · text:以文本格式返回
  93 + * (2)如果不填写参数response_format值,默认为text
  94 + * 必填:否
  95 + */
  96 + private String response_format;
  97 + /**
  98 + * 表示最终用户的唯一标识符
  99 + * 必填:否
  100 + */
  101 + private String user_id;
  102 + /**
  103 + * 在函数调用场景下,提示大模型选择指定的函数(非强制),说明:指定的函数名必须在functions中存在
  104 + * 必填:否
  105 + *
  106 + * ERNIE-4.0-8K 模型没有这个字段
  107 + */
  108 + private String tool_choice;
  109 +
  110 + //
  111 + // 以下兼容 spring-ai ChatOptions 暂时没有其他地方用到
  112 +
  113 + @Override
  114 + public Float getTemperature() {
  115 + return this.temperature;
  116 + }
  117 +
  118 + @Override
  119 + public void setTemperature(Float temperature) {
  120 + this.temperature = temperature;
  121 + }
  122 +
  123 + @Override
  124 + public Float getTopP() {
  125 + return top_p;
  126 + }
  127 +
  128 + @Override
  129 + public void setTopP(Float topP) {
  130 + this.top_p = topP;
  131 + }
  132 +
  133 + // 百度么有 topK
  134 +
  135 + @Override
  136 + public Integer getTopK() {
  137 + return null;
  138 + }
  139 +
  140 + @Override
  141 + public void setTopK(Integer topK) {
  142 +
  143 + }
  144 +}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionRequest.java
@@ -37,14 +37,14 @@ public class YiYanChatCompletionRequest { @@ -37,14 +37,14 @@ public class YiYanChatCompletionRequest {
37 * (2)默认0.8,范围 (0, 1.0],不能为0 37 * (2)默认0.8,范围 (0, 1.0],不能为0
38 * 必填:否 38 * 必填:否
39 */ 39 */
40 - private String temperature; 40 + private Float temperature;
41 /** 41 /**
42 * 说明: 42 * 说明:
43 * (1)影响输出文本的多样性,取值越大,生成文本的多样性越强 43 * (1)影响输出文本的多样性,取值越大,生成文本的多样性越强
44 * (2)默认0.8,取值范围 [0, 1.0] 44 * (2)默认0.8,取值范围 [0, 1.0]
45 * 必填:否 45 * 必填:否
46 */ 46 */
47 - private String top_p; 47 + private Float top_p;
48 /** 48 /**
49 * 通过对已生成的token增加惩罚,减少重复生成的现象。说明: 49 * 通过对已生成的token增加惩罚,减少重复生成的现象。说明:
50 * (1)值越大表示惩罚越大 50 * (1)值越大表示惩罚越大
@@ -52,7 +52,7 @@ public class YiYanChatCompletionRequest { @@ -52,7 +52,7 @@ public class YiYanChatCompletionRequest {
52 * 52 *
53 * 必填:否 53 * 必填:否
54 */ 54 */
55 - private String penalty_score; 55 + private Float penalty_score;
56 /** 56 /**
57 * 是否以流式接口的形式返回数据,默认false 57 * 是否以流式接口的形式返回数据,默认false
58 * 必填:否 58 * 必填:否
@@ -71,7 +71,7 @@ public class YiYanChatCompletionRequest { @@ -71,7 +71,7 @@ public class YiYanChatCompletionRequest {
71 * (2)最多4个元素 71 * (2)最多4个元素
72 * 必填:否 72 * 必填:否
73 */ 73 */
74 - private String stop; 74 + private List<String> stop;
75 /** 75 /**
76 * 是否强制关闭实时搜索功能,默认false,表示不关闭 76 * 是否强制关闭实时搜索功能,默认false,表示不关闭
77 * 必填:否 77 * 必填:否
@@ -106,6 +106,8 @@ public class YiYanChatCompletionRequest { @@ -106,6 +106,8 @@ public class YiYanChatCompletionRequest {
106 /** 106 /**
107 * 在函数调用场景下,提示大模型选择指定的函数(非强制),说明:指定的函数名必须在functions中存在 107 * 在函数调用场景下,提示大模型选择指定的函数(非强制),说明:指定的函数名必须在functions中存在
108 * 必填:否 108 * 必填:否
  109 + *
  110 + * ERNIE-4.0-8K 模型没有这个字段
109 */ 111 */
110 private String tool_choice; 112 private String tool_choice;
111 113
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 - "",  
27 - "",  
28 - "", 26 + "LTAI5tNTVhXW4fLKUjMrr98z",
  27 + "ZJ0JQeyjzxxm5CfeTV6k1wNE9UsvZP",
  28 + "f0c1088824594f589c8f10567ccd929f_p_efm",
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/YiYanChatTests.java
@@ -4,12 +4,12 @@ import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; @@ -4,12 +4,12 @@ import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
4 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanApi; 4 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanApi;
5 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient; 5 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient;
6 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel; 6 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel;
  7 +import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanOptions;
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;
10 11
11 import java.util.Scanner; 12 import java.util.Scanner;
12 -import java.util.function.Consumer;  
13 13
14 /** 14 /**
15 * chat 文心一言 15 * chat 文心一言
@@ -29,7 +29,7 @@ public class YiYanChatTests { @@ -29,7 +29,7 @@ public class YiYanChatTests {
29 YiYanChatModel.ERNIE4_3_5_8K, 29 YiYanChatModel.ERNIE4_3_5_8K,
30 86400 30 86400
31 ); 31 );
32 - yiYanChatClient = new YiYanChatClient(yiYanApi); 32 + yiYanChatClient = new YiYanChatClient(yiYanApi, new YiYanOptions().setMax_output_tokens(2048));
33 } 33 }
34 34
35 @Test 35 @Test
@@ -41,12 +41,7 @@ public class YiYanChatTests { @@ -41,12 +41,7 @@ public class YiYanChatTests {
41 @Test 41 @Test
42 public void streamTest() { 42 public void streamTest() {
43 Flux<ChatResponse> fluxResponse = yiYanChatClient.stream(new Prompt("用java帮我写一个快排算法?")); 43 Flux<ChatResponse> fluxResponse = yiYanChatClient.stream(new Prompt("用java帮我写一个快排算法?"));
44 - fluxResponse.subscribe(new Consumer<ChatResponse>() {  
45 - @Override  
46 - public void accept(ChatResponse chatResponse) {  
47 - System.err.print(chatResponse.getResult().getOutput().getContent());  
48 - }  
49 - }); 44 + fluxResponse.subscribe(chatResponse -> System.err.print(chatResponse.getResult().getOutput().getContent()));
50 // 阻止退出 45 // 阻止退出
51 Scanner scanner = new Scanner(System.in); 46 Scanner scanner = new Scanner(System.in);
52 scanner.nextLine(); 47 scanner.nextLine();