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 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 6 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
9 7 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion;
10 8 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest;
... ... @@ -18,7 +16,6 @@ import org.springframework.retry.support.RetryTemplate;
18 16 import reactor.core.publisher.Flux;
19 17  
20 18 import java.time.Duration;
21   -import java.util.ArrayList;
22 19 import java.util.List;
23 20  
24 21 /**
... ... @@ -32,10 +29,17 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
32 29  
33 30 private YiYanApi yiYanApi;
34 31  
  32 + private YiYanOptions yiYanOptions;
  33 +
35 34 public YiYanChatClient(YiYanApi yiYanApi) {
36 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 43 public final RetryTemplate retryTemplate = RetryTemplate.builder()
40 44 // 最大重试次数 10
41 45 .maxAttempts(10)
... ... @@ -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 77 @Override
88 78 public Flux<ChatResponse> stream(Prompt prompt) {
89 79 // ctx 会有重试的信息
... ... @@ -93,4 +83,35 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
93 83 Flux<YiYanChatCompletion> response = this.yiYanApi.chatCompletionStream(request);
94 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 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 43 * (1)影响输出文本的多样性,取值越大,生成文本的多样性越强
44 44 * (2)默认0.8,取值范围 [0, 1.0]
45 45 * 必填:否
46 46 */
47   - private String top_p;
  47 + private Float top_p;
48 48 /**
49 49 * 通过对已生成的token增加惩罚,减少重复生成的现象。说明:
50 50 * (1)值越大表示惩罚越大
... ... @@ -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 57 * 是否以流式接口的形式返回数据,默认false
58 58 * 必填:否
... ... @@ -71,7 +71,7 @@ public class YiYanChatCompletionRequest {
71 71 * (2)最多4个元素
72 72 * 必填:否
73 73 */
74   - private String stop;
  74 + private List<String> stop;
75 75 /**
76 76 * 是否强制关闭实时搜索功能,默认false,表示不关闭
77 77 * 必填:否
... ... @@ -106,6 +106,8 @@ public class YiYanChatCompletionRequest {
106 106 /**
107 107 * 在函数调用场景下,提示大模型选择指定的函数(非强制),说明:指定的函数名必须在functions中存在
108 108 * 必填:否
  109 + *
  110 + * ERNIE-4.0-8K 模型没有这个字段
109 111 */
110 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 23 @Before
24 24 public void setup() {
25 25 QianWenApi qianWenApi = new QianWenApi(
26   - "",
27   - "",
28   - "",
  26 + "LTAI5tNTVhXW4fLKUjMrr98z",
  27 + "ZJ0JQeyjzxxm5CfeTV6k1wNE9UsvZP",
  28 + "f0c1088824594f589c8f10567ccd929f_p_efm",
29 29 null
30 30 );
31 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 4 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanApi;
5 5 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient;
6 6 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel;
  7 +import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanOptions;
7 8 import org.junit.Before;
8 9 import org.junit.Test;
9 10 import reactor.core.publisher.Flux;
10 11  
11 12 import java.util.Scanner;
12   -import java.util.function.Consumer;
13 13  
14 14 /**
15 15 * chat 文心一言
... ... @@ -29,7 +29,7 @@ public class YiYanChatTests {
29 29 YiYanChatModel.ERNIE4_3_5_8K,
30 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 35 @Test
... ... @@ -41,12 +41,7 @@ public class YiYanChatTests {
41 41 @Test
42 42 public void streamTest() {
43 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 46 Scanner scanner = new Scanner(System.in);
52 47 scanner.nextLine();
... ...