Commit f41e43713c9ca32bf72420f6c135b5bb7ea1e01e
1 parent
1fada268
百度 文心一言 适配chatOptions
Showing
5 changed files
with
197 additions
and
35 deletions
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(); |