Commit ffe4afaaafea38e1c4ca54f1f64025b63fd23ac7
1 parent
25a0fe90
【功能新增】AI:新增 function call 示例,完成所有模型的测试 = = 累
Showing
8 changed files
with
186 additions
and
135 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| ... | ... | @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 7 | 7 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 9 | 9 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 10 | +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | |
| 10 | 11 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 11 | 12 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 12 | 13 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; |
| ... | ... | @@ -238,7 +239,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 238 | 239 | // 2. 构建 ChatOptions 对象 |
| 239 | 240 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 240 | 241 | ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), |
| 241 | - conversation.getTemperature(), conversation.getMaxTokens()); | |
| 242 | + conversation.getTemperature(), conversation.getMaxTokens(), | |
| 243 | + SetUtils.asSet("directory_list", "weather_query")); | |
| 242 | 244 | return new Prompt(chatMessages, chatOptions); |
| 243 | 245 | } |
| 244 | 246 | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.service.tool; | |
| 2 | - | |
| 3 | -import cn.hutool.core.date.LocalDateTimeUtil; | |
| 4 | -import cn.hutool.core.io.FileUtil; | |
| 5 | -import cn.hutool.core.util.ArrayUtil; | |
| 6 | -import cn.hutool.core.util.StrUtil; | |
| 7 | -import lombok.AllArgsConstructor; | |
| 8 | -import lombok.Data; | |
| 9 | -import lombok.NoArgsConstructor; | |
| 10 | -import org.springframework.ai.tool.annotation.Tool; | |
| 11 | -import org.springframework.ai.tool.annotation.ToolParam; | |
| 12 | -import org.springframework.stereotype.Component; | |
| 13 | - | |
| 14 | -import java.io.File; | |
| 15 | -import java.nio.file.Path; | |
| 16 | -import java.nio.file.Paths; | |
| 17 | -import java.util.Arrays; | |
| 18 | -import java.util.Collections; | |
| 19 | -import java.util.List; | |
| 20 | - | |
| 21 | -import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | |
| 22 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 23 | - | |
| 24 | -/** | |
| 25 | - * 目录内容列表工具:列出指定目录的内容 | |
| 26 | - * | |
| 27 | - * @author 芋道源码 | |
| 28 | - */ | |
| 29 | -@Component | |
| 30 | -public class ListDirTool { | |
| 31 | - | |
| 32 | - /** | |
| 33 | - * 列出指定目录的内容 | |
| 34 | - * | |
| 35 | - * @param relativePath 要列出内容的目录路径,相对于工作区根目录 | |
| 36 | - * @return 目录内容列表 | |
| 37 | - */ | |
| 38 | - @Tool(name = "listDir", description = "列出指定目录的内容") | |
| 39 | - public Response listDir(@ToolParam(description = "要列出内容的目录路径,相对于工作区根目录") String relativePath) { | |
| 40 | - // 校验目录存在 | |
| 41 | - String path = StrUtil.blankToDefault(relativePath, "."); | |
| 42 | - Path dirPath = Paths.get(path); | |
| 43 | - if (!FileUtil.exist(dirPath.toString()) || !FileUtil.isDirectory(dirPath.toString())) { | |
| 44 | - return new Response(Collections.emptyList()); | |
| 45 | - } | |
| 46 | - // 列出目录内容 | |
| 47 | - File[] files = dirPath.toFile().listFiles(); | |
| 48 | - if (ArrayUtil.isEmpty(files)) { | |
| 49 | - return new Response(Collections.emptyList()); | |
| 50 | - } | |
| 51 | - return new Response(convertList(Arrays.asList(files), file -> new Response.File() | |
| 52 | - .setDirectory(file.isDirectory()).setName(file.getName()) | |
| 53 | - .setSize(file.isFile() ? FileUtil.readableFileSize(file.length()) : null) | |
| 54 | - .setLastModified( | |
| 55 | - LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN)))); | |
| 56 | - } | |
| 57 | - | |
| 58 | - @Data | |
| 59 | - @AllArgsConstructor | |
| 60 | - @NoArgsConstructor | |
| 61 | - public static class Response { | |
| 62 | - | |
| 63 | - /** | |
| 64 | - * 目录内容列表 | |
| 65 | - */ | |
| 66 | - private List<File> files; | |
| 67 | - | |
| 68 | - @Data | |
| 69 | - public static class File { | |
| 70 | - | |
| 71 | - /** | |
| 72 | - * 是否为目录 | |
| 73 | - */ | |
| 74 | - private Boolean directory; | |
| 75 | - | |
| 76 | - /** | |
| 77 | - * 名称 | |
| 78 | - */ | |
| 79 | - private String name; | |
| 80 | - | |
| 81 | - /** | |
| 82 | - * 大小,仅对文件有效 | |
| 83 | - */ | |
| 84 | - private String size; | |
| 85 | - | |
| 86 | - /** | |
| 87 | - * 最后修改时间 | |
| 88 | - */ | |
| 89 | - private String lastModified; | |
| 90 | - | |
| 91 | - } | |
| 92 | - | |
| 93 | - } | |
| 94 | - | |
| 95 | -} | |
| 96 | 0 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java
| 1 | -package cn.iocoder.yudao.module.ai.service.tool; | |
| 1 | +package cn.iocoder.yudao.module.ai.service.tool.function; | |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 4 | 4 | import cn.hutool.core.io.FileUtil; |
| 5 | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | 7 | import com.fasterxml.jackson.annotation.JsonClassDescription; |
| 8 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
| 8 | 9 | import com.fasterxml.jackson.annotation.JsonPropertyDescription; |
| 9 | 10 | import lombok.AllArgsConstructor; |
| 10 | 11 | import lombok.Data; |
| ... | ... | @@ -12,8 +13,6 @@ import lombok.NoArgsConstructor; |
| 12 | 13 | import org.springframework.stereotype.Component; |
| 13 | 14 | |
| 14 | 15 | import java.io.File; |
| 15 | -import java.nio.file.Path; | |
| 16 | -import java.nio.file.Paths; | |
| 17 | 16 | import java.util.Arrays; |
| 18 | 17 | import java.util.Collections; |
| 19 | 18 | import java.util.List; |
| ... | ... | @@ -23,21 +22,22 @@ import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; |
| 23 | 22 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 24 | 23 | |
| 25 | 24 | /** |
| 26 | - * 目录内容列表工具:列出指定目录的内容 | |
| 25 | + * 工具:列出指定目录的文件列表 | |
| 27 | 26 | * |
| 28 | 27 | * @author 芋道源码 |
| 29 | 28 | */ |
| 30 | -@Component("listDir") | |
| 31 | -public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB.Response> { | |
| 29 | +@Component("directory_list") | |
| 30 | +public class DirectoryListToolFunction implements Function<DirectoryListToolFunction.Request, DirectoryListToolFunction.Response> { | |
| 32 | 31 | |
| 33 | 32 | @Data |
| 34 | - @JsonClassDescription("列出指定目录的内容") | |
| 33 | + @JsonClassDescription("列出指定目录的文件列表") | |
| 35 | 34 | public static class Request { |
| 36 | 35 | |
| 37 | 36 | /** |
| 38 | - * 要列出内容的目录路径 | |
| 37 | + * 目录路径 | |
| 39 | 38 | */ |
| 40 | - @JsonPropertyDescription("要列出内容的目录路径,例如说:/Users/yunai") | |
| 39 | + @JsonProperty(required = true, value = "path") | |
| 40 | + @JsonPropertyDescription("目录路径,例如说:/Users/yunai") | |
| 41 | 41 | private String path; |
| 42 | 42 | |
| 43 | 43 | } |
| ... | ... | @@ -48,7 +48,7 @@ public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB |
| 48 | 48 | public static class Response { |
| 49 | 49 | |
| 50 | 50 | /** |
| 51 | - * 目录内容列表 | |
| 51 | + * 文件列表 | |
| 52 | 52 | */ |
| 53 | 53 | private List<File> files; |
| 54 | 54 | |
| ... | ... | @@ -82,13 +82,12 @@ public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB |
| 82 | 82 | @Override |
| 83 | 83 | public Response apply(Request request) { |
| 84 | 84 | // 校验目录存在 |
| 85 | - String path = StrUtil.blankToDefault(request.getPath(), "."); | |
| 86 | - Path dirPath = Paths.get(path); | |
| 87 | - if (!FileUtil.exist(dirPath.toString()) || !FileUtil.isDirectory(dirPath.toString())) { | |
| 85 | + String path = StrUtil.blankToDefault(request.getPath(), "/"); | |
| 86 | + if (!FileUtil.exist(path) || !FileUtil.isDirectory(path)) { | |
| 88 | 87 | return new Response(Collections.emptyList()); |
| 89 | 88 | } |
| 90 | - // 列出目录内容 | |
| 91 | - File[] files = dirPath.toFile().listFiles(); | |
| 89 | + // 列出目录 | |
| 90 | + File[] files = FileUtil.ls(path); | |
| 92 | 91 | if (ArrayUtil.isEmpty(files)) { |
| 93 | 92 | return new Response(Collections.emptyList()); |
| 94 | 93 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/WeatherQueryToolFunction.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.tool.function; | |
| 2 | + | |
| 3 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 4 | +import cn.hutool.core.util.RandomUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 6 | +import com.fasterxml.jackson.annotation.JsonClassDescription; | |
| 7 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
| 8 | +import com.fasterxml.jackson.annotation.JsonPropertyDescription; | |
| 9 | +import lombok.AllArgsConstructor; | |
| 10 | +import lombok.Data; | |
| 11 | +import lombok.NoArgsConstructor; | |
| 12 | +import org.springframework.stereotype.Component; | |
| 13 | + | |
| 14 | +import java.time.LocalDateTime; | |
| 15 | +import java.util.function.Function; | |
| 16 | + | |
| 17 | +import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * 工具:查询指定城市的天气信息 | |
| 21 | + * | |
| 22 | + * @author 芋道源码 | |
| 23 | + */ | |
| 24 | +@Component("weather_query") | |
| 25 | +public class WeatherQueryToolFunction | |
| 26 | + implements Function<WeatherQueryToolFunction.Request, WeatherQueryToolFunction.Response> { | |
| 27 | + | |
| 28 | + private static final String[] WEATHER_CONDITIONS = { "晴朗", "多云", "阴天", "小雨", "大雨", "雷雨", "小雪", "大雪" }; | |
| 29 | + | |
| 30 | + @Data | |
| 31 | + @JsonClassDescription("查询指定城市的天气信息") | |
| 32 | + public static class Request { | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * 城市名称 | |
| 36 | + */ | |
| 37 | + @JsonProperty(required = true, value = "city") | |
| 38 | + @JsonPropertyDescription("城市名称,例如:北京、上海、广州") | |
| 39 | + private String city; | |
| 40 | + | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Data | |
| 44 | + @AllArgsConstructor | |
| 45 | + @NoArgsConstructor | |
| 46 | + public static class Response { | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 城市名称 | |
| 50 | + */ | |
| 51 | + private String city; | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 天气信息 | |
| 55 | + */ | |
| 56 | + private WeatherInfo weatherInfo; | |
| 57 | + | |
| 58 | + @Data | |
| 59 | + @AllArgsConstructor | |
| 60 | + @NoArgsConstructor | |
| 61 | + public static class WeatherInfo { | |
| 62 | + | |
| 63 | + /** | |
| 64 | + * 温度(摄氏度) | |
| 65 | + */ | |
| 66 | + private Integer temperature; | |
| 67 | + | |
| 68 | + /** | |
| 69 | + * 天气状况 | |
| 70 | + */ | |
| 71 | + private String condition; | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * 湿度百分比 | |
| 75 | + */ | |
| 76 | + private Integer humidity; | |
| 77 | + | |
| 78 | + /** | |
| 79 | + * 风速(km/h) | |
| 80 | + */ | |
| 81 | + private Integer windSpeed; | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * 查询时间 | |
| 85 | + */ | |
| 86 | + private String queryTime; | |
| 87 | + | |
| 88 | + } | |
| 89 | + | |
| 90 | + } | |
| 91 | + | |
| 92 | + @Override | |
| 93 | + public Response apply(Request request) { | |
| 94 | + // 检查城市名称是否为空 | |
| 95 | + if (StrUtil.isBlank(request.getCity())) { | |
| 96 | + return new Response("未知城市", null); | |
| 97 | + } | |
| 98 | + | |
| 99 | + // 获取天气数据 | |
| 100 | + String city = request.getCity(); | |
| 101 | + Response.WeatherInfo weatherInfo = generateMockWeatherInfo(); | |
| 102 | + return new Response(city, weatherInfo); | |
| 103 | + } | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 生成模拟的天气数据 | |
| 107 | + * 在实际应用中,应替换为真实 API 调用 | |
| 108 | + */ | |
| 109 | + private Response.WeatherInfo generateMockWeatherInfo() { | |
| 110 | + int temperature = RandomUtil.randomInt(-5, 30); | |
| 111 | + int humidity = RandomUtil.randomInt(1, 100); | |
| 112 | + int windSpeed = RandomUtil.randomInt(1, 30); | |
| 113 | + String condition = RandomUtil.randomEle(WEATHER_CONDITIONS); | |
| 114 | + return new Response.WeatherInfo(temperature, condition, humidity, windSpeed, | |
| 115 | + LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN)); | |
| 116 | + } | |
| 117 | + | |
| 118 | +} | |
| 0 | 119 | \ No newline at end of file | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java
0 → 100644
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
| 1 | 1 | package cn.iocoder.yudao.framework.ai.config; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | +import cn.hutool.extra.spring.SpringUtil; | |
| 4 | 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; |
| 5 | 6 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; |
| 6 | 7 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; |
| ... | ... | @@ -17,6 +18,7 @@ import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStore |
| 17 | 18 | import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; |
| 18 | 19 | import org.springframework.ai.embedding.BatchingStrategy; |
| 19 | 20 | import org.springframework.ai.embedding.TokenCountBatchingStrategy; |
| 21 | +import org.springframework.ai.model.tool.ToolCallingManager; | |
| 20 | 22 | import org.springframework.ai.openai.OpenAiChatModel; |
| 21 | 23 | import org.springframework.ai.openai.OpenAiChatOptions; |
| 22 | 24 | import org.springframework.ai.openai.api.OpenAiApi; |
| ... | ... | @@ -70,6 +72,7 @@ public class YudaoAiAutoConfiguration { |
| 70 | 72 | .maxTokens(properties.getMaxTokens()) |
| 71 | 73 | .topP(properties.getTopP()) |
| 72 | 74 | .build()) |
| 75 | + .toolCallingManager(getToolCallingManager()) | |
| 73 | 76 | .build(); |
| 74 | 77 | return new DeepSeekChatModel(openAiChatModel); |
| 75 | 78 | } |
| ... | ... | @@ -96,6 +99,7 @@ public class YudaoAiAutoConfiguration { |
| 96 | 99 | .maxTokens(properties.getMaxTokens()) |
| 97 | 100 | .topP(properties.getTopP()) |
| 98 | 101 | .build()) |
| 102 | + .toolCallingManager(getToolCallingManager()) | |
| 99 | 103 | .build(); |
| 100 | 104 | return new DouBaoChatModel(openAiChatModel); |
| 101 | 105 | } |
| ... | ... | @@ -122,6 +126,7 @@ public class YudaoAiAutoConfiguration { |
| 122 | 126 | .maxTokens(properties.getMaxTokens()) |
| 123 | 127 | .topP(properties.getTopP()) |
| 124 | 128 | .build()) |
| 129 | + .toolCallingManager(getToolCallingManager()) | |
| 125 | 130 | .build(); |
| 126 | 131 | return new SiliconFlowChatModel(openAiChatModel); |
| 127 | 132 | } |
| ... | ... | @@ -155,6 +160,7 @@ public class YudaoAiAutoConfiguration { |
| 155 | 160 | .maxTokens(properties.getMaxTokens()) |
| 156 | 161 | .topP(properties.getTopP()) |
| 157 | 162 | .build()) |
| 163 | + .toolCallingManager(getToolCallingManager()) | |
| 158 | 164 | .build(); |
| 159 | 165 | return new HunYuanChatModel(openAiChatModel); |
| 160 | 166 | } |
| ... | ... | @@ -181,6 +187,7 @@ public class YudaoAiAutoConfiguration { |
| 181 | 187 | .maxTokens(properties.getMaxTokens()) |
| 182 | 188 | .topP(properties.getTopP()) |
| 183 | 189 | .build()) |
| 190 | + .toolCallingManager(getToolCallingManager()) | |
| 184 | 191 | .build(); |
| 185 | 192 | return new XingHuoChatModel(openAiChatModel); |
| 186 | 193 | } |
| ... | ... | @@ -210,4 +217,8 @@ public class YudaoAiAutoConfiguration { |
| 210 | 217 | return new TokenCountBatchingStrategy(); |
| 211 | 218 | } |
| 212 | 219 | |
| 220 | + private static ToolCallingManager getToolCallingManager() { | |
| 221 | + return SpringUtil.getBean(ToolCallingManager.class); | |
| 222 | + } | |
| 223 | + | |
| 213 | 224 | } |
| 214 | 225 | \ No newline at end of file | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
| ... | ... | @@ -23,6 +23,7 @@ import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; |
| 23 | 23 | import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; |
| 24 | 24 | import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; |
| 25 | 25 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; |
| 26 | +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; | |
| 26 | 27 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; |
| 27 | 28 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; |
| 28 | 29 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; |
| ... | ... | @@ -58,11 +59,14 @@ import org.springframework.ai.embedding.BatchingStrategy; |
| 58 | 59 | import org.springframework.ai.embedding.EmbeddingModel; |
| 59 | 60 | import org.springframework.ai.image.ImageModel; |
| 60 | 61 | import org.springframework.ai.minimax.MiniMaxChatModel; |
| 62 | +import org.springframework.ai.minimax.MiniMaxChatOptions; | |
| 61 | 63 | import org.springframework.ai.minimax.MiniMaxEmbeddingModel; |
| 62 | 64 | import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; |
| 63 | 65 | import org.springframework.ai.minimax.api.MiniMaxApi; |
| 66 | +import org.springframework.ai.model.function.FunctionCallbackResolver; | |
| 64 | 67 | import org.springframework.ai.model.tool.ToolCallingManager; |
| 65 | 68 | import org.springframework.ai.moonshot.MoonshotChatModel; |
| 69 | +import org.springframework.ai.moonshot.MoonshotChatOptions; | |
| 66 | 70 | import org.springframework.ai.moonshot.api.MoonshotApi; |
| 67 | 71 | import org.springframework.ai.ollama.OllamaChatModel; |
| 68 | 72 | import org.springframework.ai.ollama.OllamaEmbeddingModel; |
| ... | ... | @@ -90,10 +94,7 @@ import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservat |
| 90 | 94 | import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; |
| 91 | 95 | import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; |
| 92 | 96 | import org.springframework.ai.vectorstore.redis.RedisVectorStore; |
| 93 | -import org.springframework.ai.zhipuai.ZhiPuAiChatModel; | |
| 94 | -import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingModel; | |
| 95 | -import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingOptions; | |
| 96 | -import org.springframework.ai.zhipuai.ZhiPuAiImageModel; | |
| 97 | +import org.springframework.ai.zhipuai.*; | |
| 97 | 98 | import org.springframework.ai.zhipuai.api.ZhiPuAiApi; |
| 98 | 99 | import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; |
| 99 | 100 | import org.springframework.beans.BeansException; |
| ... | ... | @@ -110,6 +111,7 @@ import java.util.Timer; |
| 110 | 111 | import java.util.TimerTask; |
| 111 | 112 | |
| 112 | 113 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 114 | +import static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE; | |
| 113 | 115 | |
| 114 | 116 | /** |
| 115 | 117 | * AI Model 模型工厂的实现类 |
| ... | ... | @@ -308,7 +310,9 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 308 | 310 | */ |
| 309 | 311 | private static DashScopeChatModel buildTongYiChatModel(String key) { |
| 310 | 312 | DashScopeApi dashScopeApi = new DashScopeApi(key); |
| 311 | - return new DashScopeChatModel(dashScopeApi); | |
| 313 | + DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL) | |
| 314 | + .withTemperature(0.7).build(); | |
| 315 | + return new DashScopeChatModel(dashScopeApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | |
| 312 | 316 | } |
| 313 | 317 | |
| 314 | 318 | /** |
| ... | ... | @@ -385,7 +389,8 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 385 | 389 | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { |
| 386 | 390 | ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) |
| 387 | 391 | : new ZhiPuAiApi(url, apiKey); |
| 388 | - return new ZhiPuAiChatModel(zhiPuAiApi); | |
| 392 | + ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); | |
| 393 | + return new ZhiPuAiChatModel(zhiPuAiApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | |
| 389 | 394 | } |
| 390 | 395 | |
| 391 | 396 | /** |
| ... | ... | @@ -403,7 +408,8 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 403 | 408 | private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { |
| 404 | 409 | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) |
| 405 | 410 | : new MiniMaxApi(url, apiKey); |
| 406 | - return new MiniMaxChatModel(miniMaxApi); | |
| 411 | + MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); | |
| 412 | + return new MiniMaxChatModel(miniMaxApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | |
| 407 | 413 | } |
| 408 | 414 | |
| 409 | 415 | /** |
| ... | ... | @@ -412,7 +418,8 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 412 | 418 | private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { |
| 413 | 419 | MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey) |
| 414 | 420 | : new MoonshotApi(url, apiKey); |
| 415 | - return new MoonshotChatModel(moonshotApi); | |
| 421 | + MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build(); | |
| 422 | + return new MoonshotChatModel(moonshotApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | |
| 416 | 423 | } |
| 417 | 424 | |
| 418 | 425 | /** |
| ... | ... | @@ -449,7 +456,7 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 449 | 456 | // 获取 AzureOpenAiChatProperties 对象 |
| 450 | 457 | AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class); |
| 451 | 458 | return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, |
| 452 | - null, null, null); | |
| 459 | + getToolCallingManager(), null, null); | |
| 453 | 460 | } |
| 454 | 461 | |
| 455 | 462 | /** |
| ... | ... | @@ -704,4 +711,8 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 704 | 711 | return SpringUtil.getBean(ToolCallingManager.class); |
| 705 | 712 | } |
| 706 | 713 | |
| 714 | + private static FunctionCallbackResolver getFunctionCallbackResolver() { | |
| 715 | + return SpringUtil.getBean(FunctionCallbackResolver.class); | |
| 716 | + } | |
| 717 | + | |
| 707 | 718 | } | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java
| ... | ... | @@ -13,6 +13,8 @@ import org.springframework.ai.openai.OpenAiChatOptions; |
| 13 | 13 | import org.springframework.ai.qianfan.QianFanChatOptions; |
| 14 | 14 | import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; |
| 15 | 15 | |
| 16 | +import java.util.Set; | |
| 17 | + | |
| 16 | 18 | /** |
| 17 | 19 | * Spring AI 工具类 |
| 18 | 20 | * |
| ... | ... | @@ -21,22 +23,27 @@ import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; |
| 21 | 23 | public class AiUtils { |
| 22 | 24 | |
| 23 | 25 | public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { |
| 26 | + return buildChatOptions(platform, model, temperature, maxTokens, null); | |
| 27 | + } | |
| 28 | + | |
| 29 | + public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens, | |
| 30 | + Set<String> toolNames) { | |
| 24 | 31 | // noinspection EnhancedSwitchMigration |
| 25 | 32 | switch (platform) { |
| 26 | 33 | case TONG_YI: |
| 27 | - // TODO functions | |
| 28 | - return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens).build(); | |
| 34 | + return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) | |
| 35 | + .withFunctions(toolNames).build(); | |
| 29 | 36 | case YI_YAN: |
| 30 | 37 | return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); |
| 31 | 38 | case ZHI_PU: |
| 32 | - // TODO functions | |
| 33 | - return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | |
| 39 | + return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | |
| 40 | + .functions(toolNames).build(); | |
| 34 | 41 | case MINI_MAX: |
| 35 | - // TODO functions | |
| 36 | - return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | |
| 42 | + return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | |
| 43 | + .functions(toolNames).build(); | |
| 37 | 44 | case MOONSHOT: |
| 38 | - // TODO functions | |
| 39 | - return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | |
| 45 | + return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | |
| 46 | + .functions(toolNames).build(); | |
| 40 | 47 | case OPENAI: |
| 41 | 48 | case DEEP_SEEK: // 复用 OpenAI 客户端 |
| 42 | 49 | case DOU_BAO: // 复用 OpenAI 客户端 |
| ... | ... | @@ -44,17 +51,14 @@ public class AiUtils { |
| 44 | 51 | case XING_HUO: // 复用 OpenAI 客户端 |
| 45 | 52 | case SILICON_FLOW: // 复用 OpenAI 客户端 |
| 46 | 53 | return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 47 | -// .toolNames() TODO | |
| 48 | - .toolNames("listDir") | |
| 49 | - .build(); | |
| 54 | + .toolNames(toolNames).build(); | |
| 50 | 55 | case AZURE_OPENAI: |
| 51 | 56 | // TODO 芋艿:貌似没 model 字段???! |
| 52 | - // TODO 芋艿:.toolNames() TODO | |
| 53 | - return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens).build(); | |
| 57 | + return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) | |
| 58 | + .toolNames(toolNames).build(); | |
| 54 | 59 | case OLLAMA: |
| 55 | - // TODO 芋艿:.toolNames() TODO | |
| 56 | 60 | return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) |
| 57 | - .toolNames("listDir").build(); | |
| 61 | + .toolNames(toolNames).build(); | |
| 58 | 62 | default: |
| 59 | 63 | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| 60 | 64 | } | ... | ... |