mirror of
https://gitee.com/zhijiantianya/yudao-cloud.git
synced 2026-10-06 07:22:58 +08:00
feat(ai): 优化大模型兼容接入
This commit is contained in:
+1
@@ -12,6 +12,7 @@ public interface ErrorCodeConstants {
|
||||
// ========== API 密钥 1-040-000-000 ==========
|
||||
ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
|
||||
ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!");
|
||||
ErrorCode API_CONFIG_PLACEHOLDER_NOT_RESOLVED = new ErrorCode(1_040_000_002, "AI 配置({})无法解析,请检查环境变量或配置项");
|
||||
|
||||
// ========== API 模型 1-040-001-000 ==========
|
||||
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ public enum AiPlatformEnum implements ArrayValuable<String> {
|
||||
MINI_MAX("MiniMax", "MiniMax"), // 稀宇科技
|
||||
MOONSHOT("Moonshot", "月之暗面"), // KIMI
|
||||
BAI_CHUAN("BaiChuan", "百川智能"), // 百川智能
|
||||
STEP_FUN("StepFun", "阶跃星辰"), // 阶跃星辰
|
||||
|
||||
// ========== 国外平台 ==========
|
||||
|
||||
@@ -40,7 +41,7 @@ public enum AiPlatformEnum implements ArrayValuable<String> {
|
||||
STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI
|
||||
MIDJOURNEY("Midjourney", "Midjourney"), // Midjourney
|
||||
SUNO("Suno", "Suno"), // Suno AI
|
||||
GROK("Grok","Grok"), // Grok
|
||||
GROK("Grok", "Grok"), // Grok
|
||||
|
||||
;
|
||||
|
||||
|
||||
@@ -19,12 +19,24 @@
|
||||
国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
|
||||
</description>
|
||||
<properties>
|
||||
<spring-ai.version>1.1.5</spring-ai.version>
|
||||
<spring-ai.version>1.1.8</spring-ai.version>
|
||||
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud.ai/spring-ai-alibaba -->
|
||||
<alibaba-ai.version>1.1.2.2</alibaba-ai.version>
|
||||
<tinyflow.version>1.2.6</tinyflow.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-bom</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud 基础 -->
|
||||
<dependency>
|
||||
@@ -147,18 +159,6 @@
|
||||
<artifactId>spring-ai-starter-model-stability-ai</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- 智谱 GLM -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-minimax</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- 通义千问 -->
|
||||
<groupId>com.alibaba.cloud.ai</groupId>
|
||||
@@ -166,19 +166,6 @@
|
||||
<version>${alibaba-ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- 文心一言 -->
|
||||
<groupId>org.springaicommunity</groupId>
|
||||
<artifactId>qianfan-spring-boot-starter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- 月之暗面 -->
|
||||
<groupId>org.springaicommunity</groupId>
|
||||
<artifactId>moonshot-spring-boot-starter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 向量存储:https://db-engines.com/en/ranking/vector+dbms -->
|
||||
<dependency>
|
||||
<!-- Qdrant:https://qdrant.tech/ -->
|
||||
@@ -315,4 +302,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
+144
-30
@@ -10,10 +10,15 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
|
||||
import cn.iocoder.yudao.module.ai.tool.method.PersonService;
|
||||
@@ -44,7 +49,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 芋道 AI 自动配置
|
||||
@@ -113,13 +117,13 @@ public class AiAutoConfiguration {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||
.openAiApi(OpenAiApi.builder()
|
||||
DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(DouBaoChatModel.BASE_URL)
|
||||
.completionsPath(DouBaoChatModel.COMPLETE_PATH)
|
||||
.apiKey(properties.getApiKey())
|
||||
.build())
|
||||
.defaultOptions(OpenAiChatOptions.builder()
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(properties.getModel())
|
||||
.temperature(properties.getTemperature())
|
||||
.maxTokens(properties.getMaxTokens())
|
||||
@@ -168,11 +172,8 @@ public class AiAutoConfiguration {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
// 特殊:由于混元大模型不提供 deepseek,而是通过知识引擎,所以需要区分下 URL
|
||||
if (StrUtil.isEmpty(properties.getBaseUrl())) {
|
||||
properties.setBaseUrl(
|
||||
StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL
|
||||
: HunYuanChatModel.BASE_URL);
|
||||
properties.setBaseUrl(HunYuanChatModel.BASE_URL);
|
||||
}
|
||||
// 创建 DeepSeekChatModel、HunYuanChatModel 对象
|
||||
DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
|
||||
@@ -203,25 +204,15 @@ public class AiAutoConfiguration {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
OpenAiApi.Builder builder = OpenAiApi.builder()
|
||||
.baseUrl(XingHuoChatModel.BASE_URL_V1)
|
||||
.apiKey(properties.getAppKey() + ":" + properties.getSecretKey());
|
||||
if ("x1".equals(properties.getModel())) {
|
||||
builder.baseUrl(XingHuoChatModel.BASE_URL_V2)
|
||||
.completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2);
|
||||
}
|
||||
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||
.openAiApi(builder.build())
|
||||
.defaultOptions(OpenAiChatOptions.builder()
|
||||
return XingHuoChatModel.builder()
|
||||
.apiKey(properties.getApiKey())
|
||||
.options(DeepSeekChatOptions.builder()
|
||||
.model(properties.getModel())
|
||||
.temperature(properties.getTemperature())
|
||||
.maxTokens(properties.getMaxTokens())
|
||||
.topP(properties.getTopP())
|
||||
.build())
|
||||
// TODO @芋艿:星火的 function call 有 bug,会报 ToolResponseMessage must have an id 错误!!!
|
||||
.toolCallingManager(getToolCallingManager())
|
||||
.build();
|
||||
return new XingHuoChatModel(openAiChatModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -235,12 +226,13 @@ public class AiAutoConfiguration {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||
.openAiApi(OpenAiApi.builder()
|
||||
DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(BaiChuanChatModel.BASE_URL)
|
||||
.apiKey(properties.getApiKey())
|
||||
.completionsPath(BaiChuanChatModel.COMPLETE_PATH)
|
||||
.build())
|
||||
.defaultOptions(OpenAiChatOptions.builder()
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(properties.getModel())
|
||||
.temperature(properties.getTemperature())
|
||||
.maxTokens(properties.getMaxTokens())
|
||||
@@ -248,7 +240,123 @@ public class AiAutoConfiguration {
|
||||
.build())
|
||||
.toolCallingManager(getToolCallingManager())
|
||||
.build();
|
||||
return new BaiChuanChatModel(openAiChatModel);
|
||||
return new BaiChuanChatModel(deepSeekChatModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "yudao.ai.yiyan.enable", havingValue = "true")
|
||||
public YiYanChatModel yiYanChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||
YudaoAiProperties.YiYan properties = yudaoAiProperties.getYiyan();
|
||||
return buildYiYanChatClient(properties);
|
||||
}
|
||||
|
||||
public YiYanChatModel buildYiYanChatClient(YudaoAiProperties.YiYan properties) {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(YiYanChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
return new YiYanChatModel(buildDeepSeekCompatibleChatModel(
|
||||
StrUtil.blankToDefault(properties.getBaseUrl(), YiYanChatModel.BASE_URL),
|
||||
null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
|
||||
properties.getMaxTokens(), properties.getTopP()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "yudao.ai.zhipu.enable", havingValue = "true")
|
||||
public ZhiPuChatModel zhiPuChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||
YudaoAiProperties.ZhiPu properties = yudaoAiProperties.getZhipu();
|
||||
return buildZhiPuChatClient(properties);
|
||||
}
|
||||
|
||||
public ZhiPuChatModel buildZhiPuChatClient(YudaoAiProperties.ZhiPu properties) {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(ZhiPuChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), ZhiPuChatModel.BASE_URL))
|
||||
.apiKey(properties.getApiKey())
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(properties.getModel())
|
||||
.temperature(properties.getTemperature())
|
||||
.maxTokens(properties.getMaxTokens())
|
||||
.topP(properties.getTopP())
|
||||
.build())
|
||||
.build();
|
||||
return new ZhiPuChatModel(deepSeekChatModel);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "yudao.ai.minimax.enable", havingValue = "true")
|
||||
public MiniMaxChatModel miniMaxChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||
YudaoAiProperties.MiniMax properties = yudaoAiProperties.getMinimax();
|
||||
return buildMiniMaxChatClient(properties);
|
||||
}
|
||||
|
||||
public MiniMaxChatModel buildMiniMaxChatClient(YudaoAiProperties.MiniMax properties) {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(MiniMaxChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
return new MiniMaxChatModel(buildDeepSeekCompatibleChatModel(
|
||||
StrUtil.blankToDefault(properties.getBaseUrl(), MiniMaxChatModel.BASE_URL),
|
||||
null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
|
||||
properties.getMaxTokens(), properties.getTopP()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "yudao.ai.moonshot.enable", havingValue = "true")
|
||||
public MoonshotChatModel moonshotChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||
YudaoAiProperties.Moonshot properties = yudaoAiProperties.getMoonshot();
|
||||
return buildMoonshotChatClient(properties);
|
||||
}
|
||||
|
||||
public MoonshotChatModel buildMoonshotChatClient(YudaoAiProperties.Moonshot properties) {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(MoonshotChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
return new MoonshotChatModel(buildDeepSeekCompatibleChatModel(
|
||||
StrUtil.blankToDefault(properties.getBaseUrl(), MoonshotChatModel.BASE_URL),
|
||||
MoonshotChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
|
||||
properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "yudao.ai.stepfun.enable", havingValue = "true")
|
||||
public StepFunChatModel stepFunChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||
YudaoAiProperties.StepFun properties = yudaoAiProperties.getStepfun();
|
||||
return buildStepFunChatClient(properties);
|
||||
}
|
||||
|
||||
public StepFunChatModel buildStepFunChatClient(YudaoAiProperties.StepFun properties) {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(StepFunChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
return new StepFunChatModel(buildDeepSeekCompatibleChatModel(
|
||||
StrUtil.blankToDefault(properties.getBaseUrl(), StepFunChatModel.BASE_URL),
|
||||
StepFunChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
|
||||
properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
|
||||
}
|
||||
|
||||
private static DeepSeekChatModel buildDeepSeekCompatibleChatModel(String baseUrl, String completionsPath,
|
||||
String apiKey, String model,
|
||||
Double temperature, Integer maxTokens,
|
||||
Double topP) {
|
||||
DeepSeekApi.Builder apiBuilder = DeepSeekApi.builder()
|
||||
.baseUrl(baseUrl)
|
||||
.apiKey(apiKey);
|
||||
if (StrUtil.isNotEmpty(completionsPath)) {
|
||||
apiBuilder.completionsPath(completionsPath);
|
||||
}
|
||||
return DeepSeekChatModel.builder()
|
||||
.deepSeekApi(apiBuilder.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(model)
|
||||
.temperature(temperature)
|
||||
.maxTokens(maxTokens)
|
||||
.topP(topP)
|
||||
.build())
|
||||
.toolCallingManager(getToolCallingManager())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -264,14 +372,20 @@ public class AiAutoConfiguration {
|
||||
return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
|
||||
}
|
||||
|
||||
public ChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "yudao.ai.grok.enable", havingValue = "true")
|
||||
public GrokChatModel grokChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||
YudaoAiProperties.Grok properties = yudaoAiProperties.getGrok();
|
||||
return buildGrokChatClient(properties);
|
||||
}
|
||||
|
||||
public GrokChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
|
||||
if (StrUtil.isEmpty(properties.getModel())) {
|
||||
properties.setModel(GrokChatModel.MODEL_DEFAULT);
|
||||
}
|
||||
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||
.openAiApi(OpenAiApi.builder()
|
||||
.baseUrl(Optional.ofNullable(properties.getBaseUrl())
|
||||
.orElse(GrokChatModel.BASE_URL))
|
||||
.baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), GrokChatModel.BASE_URL))
|
||||
.completionsPath(GrokChatModel.COMPLETE_PATH)
|
||||
.apiKey(properties.getApiKey())
|
||||
.build())
|
||||
@@ -283,7 +397,7 @@ public class AiAutoConfiguration {
|
||||
.build())
|
||||
.toolCallingManager(getToolCallingManager())
|
||||
.build();
|
||||
return new DouBaoChatModel(openAiChatModel);
|
||||
return new GrokChatModel(openAiChatModel);
|
||||
}
|
||||
|
||||
// ========== RAG 相关 ==========
|
||||
@@ -320,4 +434,4 @@ public class AiAutoConfiguration {
|
||||
return List.of(ToolCallbacks.from(personService));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+101
-3
@@ -43,6 +43,36 @@ public class YudaoAiProperties {
|
||||
*/
|
||||
private BaiChuan baichuan;
|
||||
|
||||
/**
|
||||
* 文心一言
|
||||
*/
|
||||
private YiYan yiyan;
|
||||
|
||||
/**
|
||||
* 智谱
|
||||
*/
|
||||
private ZhiPu zhipu;
|
||||
|
||||
/**
|
||||
* MiniMax
|
||||
*/
|
||||
private MiniMax minimax;
|
||||
|
||||
/**
|
||||
* 月之暗面
|
||||
*/
|
||||
private Moonshot moonshot;
|
||||
|
||||
/**
|
||||
* 阶跃星辰
|
||||
*/
|
||||
private StepFun stepfun;
|
||||
|
||||
/**
|
||||
* Grok
|
||||
*/
|
||||
private Grok grok;
|
||||
|
||||
/**
|
||||
* Midjourney 绘图
|
||||
*/
|
||||
@@ -116,9 +146,7 @@ public class YudaoAiProperties {
|
||||
public static class XingHuo {
|
||||
|
||||
private String enable;
|
||||
private String appId;
|
||||
private String appKey;
|
||||
private String secretKey;
|
||||
private String apiKey;
|
||||
|
||||
private String model;
|
||||
private Double temperature;
|
||||
@@ -140,6 +168,76 @@ public class YudaoAiProperties {
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class YiYan {
|
||||
|
||||
private String enable;
|
||||
private String baseUrl;
|
||||
private String apiKey;
|
||||
|
||||
private String model;
|
||||
private Double temperature;
|
||||
private Integer maxTokens;
|
||||
private Double topP;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ZhiPu {
|
||||
|
||||
private String enable;
|
||||
private String baseUrl;
|
||||
private String apiKey;
|
||||
|
||||
private String model;
|
||||
private Double temperature;
|
||||
private Integer maxTokens;
|
||||
private Double topP;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class MiniMax {
|
||||
|
||||
private String enable;
|
||||
private String baseUrl;
|
||||
private String apiKey;
|
||||
|
||||
private String model;
|
||||
private Double temperature;
|
||||
private Integer maxTokens;
|
||||
private Double topP;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Moonshot {
|
||||
|
||||
private String enable;
|
||||
private String baseUrl;
|
||||
private String apiKey;
|
||||
|
||||
private String model;
|
||||
private Double temperature;
|
||||
private Integer maxTokens;
|
||||
private Double topP;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class StepFun {
|
||||
|
||||
private String enable;
|
||||
private String apiKey;
|
||||
private String baseUrl;
|
||||
|
||||
private String model;
|
||||
private Double temperature;
|
||||
private Integer maxTokens;
|
||||
private Double topP;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Midjourney {
|
||||
|
||||
|
||||
+68
-152
@@ -1,7 +1,6 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Singleton;
|
||||
import cn.hutool.core.lang.func.Func0;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
@@ -15,14 +14,21 @@ import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
|
||||
import cn.iocoder.yudao.module.ai.util.AiUtils;
|
||||
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration;
|
||||
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration;
|
||||
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration;
|
||||
@@ -40,18 +46,6 @@ import io.milvus.client.MilvusServiceClient;
|
||||
import io.qdrant.client.QdrantClient;
|
||||
import io.qdrant.client.QdrantGrpcClient;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springaicommunity.moonshot.MoonshotChatModel;
|
||||
import org.springaicommunity.moonshot.MoonshotChatOptions;
|
||||
import org.springaicommunity.moonshot.api.MoonshotApi;
|
||||
import org.springaicommunity.moonshot.autoconfigure.MoonshotChatAutoConfiguration;
|
||||
import org.springaicommunity.qianfan.QianFanChatModel;
|
||||
import org.springaicommunity.qianfan.QianFanEmbeddingModel;
|
||||
import org.springaicommunity.qianfan.QianFanEmbeddingOptions;
|
||||
import org.springaicommunity.qianfan.QianFanImageModel;
|
||||
import org.springaicommunity.qianfan.api.QianFanApi;
|
||||
import org.springaicommunity.qianfan.api.QianFanImageApi;
|
||||
import org.springaicommunity.qianfan.autoconfigure.QianFanChatAutoConfiguration;
|
||||
import org.springaicommunity.qianfan.autoconfigure.QianFanEmbeddingAutoConfiguration;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
@@ -63,27 +57,17 @@ import org.springframework.ai.embedding.BatchingStrategy;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
|
||||
import org.springframework.ai.image.ImageModel;
|
||||
import org.springframework.ai.minimax.MiniMaxChatModel;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingModel;
|
||||
import org.springframework.ai.minimax.MiniMaxEmbeddingOptions;
|
||||
import org.springframework.ai.minimax.api.MiniMaxApi;
|
||||
import org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration;
|
||||
import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration;
|
||||
import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration;
|
||||
import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties;
|
||||
import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration;
|
||||
import org.springframework.ai.model.minimax.autoconfigure.MiniMaxChatAutoConfiguration;
|
||||
import org.springframework.ai.model.minimax.autoconfigure.MiniMaxEmbeddingAutoConfiguration;
|
||||
import org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration;
|
||||
import org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration;
|
||||
import org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration;
|
||||
import org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration;
|
||||
import org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration;
|
||||
import org.springframework.ai.model.tool.ToolCallingManager;
|
||||
import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration;
|
||||
import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiEmbeddingAutoConfiguration;
|
||||
import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiImageAutoConfiguration;
|
||||
import org.springframework.ai.ollama.OllamaChatModel;
|
||||
import org.springframework.ai.ollama.OllamaEmbeddingModel;
|
||||
import org.springframework.ai.ollama.api.OllamaApi;
|
||||
@@ -114,13 +98,9 @@ import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStore
|
||||
import org.springframework.ai.vectorstore.redis.RedisVectorStore;
|
||||
import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration;
|
||||
import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties;
|
||||
import org.springframework.ai.zhipuai.*;
|
||||
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
|
||||
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import redis.clients.jedis.JedisPooled;
|
||||
|
||||
import java.io.File;
|
||||
@@ -131,7 +111,6 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE;
|
||||
|
||||
/**
|
||||
* AI Model 模型工厂的实现类
|
||||
@@ -141,7 +120,9 @@ import static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE;
|
||||
public class AiModelFactoryImpl implements AiModelFactory {
|
||||
|
||||
@Override
|
||||
public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url) {
|
||||
public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String rawApiKey, String rawUrl) {
|
||||
final String apiKey = resolveSpringPlaceholders(rawApiKey);
|
||||
final String url = resolveSpringPlaceholders(rawUrl);
|
||||
String cacheKey = buildClientCacheKey(ChatModel.class, platform, apiKey, url);
|
||||
return Singleton.get(cacheKey, (Func0<ChatModel>) () -> {
|
||||
// noinspection EnhancedSwitchMigration
|
||||
@@ -164,6 +145,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return buildMiniMaxChatModel(apiKey, url);
|
||||
case MOONSHOT:
|
||||
return buildMoonshotChatModel(apiKey, url);
|
||||
case STEP_FUN:
|
||||
return buildStepFunChatModel(apiKey, url);
|
||||
case XING_HUO:
|
||||
return buildXingHuoChatModel(apiKey);
|
||||
case BAI_CHUAN:
|
||||
@@ -179,7 +162,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
case OLLAMA:
|
||||
return buildOllamaChatModel(url);
|
||||
case GROK:
|
||||
return buildGrokChatModel(apiKey,url);
|
||||
return buildGrokChatModel(apiKey, url);
|
||||
default:
|
||||
throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
|
||||
}
|
||||
@@ -193,7 +176,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
case TONG_YI:
|
||||
return SpringUtil.getBean(DashScopeChatModel.class);
|
||||
case YI_YAN:
|
||||
return SpringUtil.getBean(QianFanChatModel.class);
|
||||
return SpringUtil.getBean(YiYanChatModel.class);
|
||||
case DEEP_SEEK:
|
||||
return SpringUtil.getBean(DeepSeekChatModel.class);
|
||||
case DOU_BAO:
|
||||
@@ -203,11 +186,13 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
case SILICON_FLOW:
|
||||
return SpringUtil.getBean(SiliconFlowChatModel.class);
|
||||
case ZHI_PU:
|
||||
return SpringUtil.getBean(ZhiPuAiChatModel.class);
|
||||
return SpringUtil.getBean(ZhiPuChatModel.class);
|
||||
case MINI_MAX:
|
||||
return SpringUtil.getBean(MiniMaxChatModel.class);
|
||||
case MOONSHOT:
|
||||
return SpringUtil.getBean(MoonshotChatModel.class);
|
||||
case STEP_FUN:
|
||||
return SpringUtil.getBean(StepFunChatModel.class);
|
||||
case XING_HUO:
|
||||
return SpringUtil.getBean(XingHuoChatModel.class);
|
||||
case BAI_CHUAN:
|
||||
@@ -222,6 +207,8 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return SpringUtil.getBean(GeminiChatModel.class);
|
||||
case OLLAMA:
|
||||
return SpringUtil.getBean(OllamaChatModel.class);
|
||||
case GROK:
|
||||
return SpringUtil.getBean(GrokChatModel.class);
|
||||
default:
|
||||
throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
|
||||
}
|
||||
@@ -233,10 +220,6 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
switch (platform) {
|
||||
case TONG_YI:
|
||||
return SpringUtil.getBean(DashScopeImageModel.class);
|
||||
case YI_YAN:
|
||||
return SpringUtil.getBean(QianFanImageModel.class);
|
||||
case ZHI_PU:
|
||||
return SpringUtil.getBean(ZhiPuAiImageModel.class);
|
||||
case SILICON_FLOW:
|
||||
return SpringUtil.getBean(SiliconFlowImageModel.class);
|
||||
case OPENAI:
|
||||
@@ -249,19 +232,17 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url) {
|
||||
public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String rawApiKey, String rawUrl) {
|
||||
String apiKey = resolveSpringPlaceholders(rawApiKey);
|
||||
String url = resolveSpringPlaceholders(rawUrl);
|
||||
// noinspection EnhancedSwitchMigration
|
||||
switch (platform) {
|
||||
case TONG_YI:
|
||||
return buildTongYiImagesModel(apiKey);
|
||||
case YI_YAN:
|
||||
return buildQianFanImageModel(apiKey);
|
||||
case ZHI_PU:
|
||||
return buildZhiPuAiImageModel(apiKey, url);
|
||||
case OPENAI:
|
||||
return buildOpenAiImageModel(apiKey, url);
|
||||
case SILICON_FLOW:
|
||||
return buildSiliconFlowImageModel(apiKey,url);
|
||||
return buildSiliconFlowImageModel(apiKey, url);
|
||||
case STABLE_DIFFUSION:
|
||||
return buildStabilityAiImageModel(apiKey, url);
|
||||
default:
|
||||
@@ -270,9 +251,11 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url) {
|
||||
String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey,
|
||||
url);
|
||||
public MidjourneyApi getOrCreateMidjourneyApi(String rawApiKey, String rawUrl) {
|
||||
final String apiKey = resolveSpringPlaceholders(rawApiKey);
|
||||
final String url = resolveSpringPlaceholders(rawUrl);
|
||||
String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(),
|
||||
apiKey, url);
|
||||
return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> {
|
||||
YudaoAiProperties.Midjourney properties = SpringUtil.getBean(YudaoAiProperties.class)
|
||||
.getMidjourney();
|
||||
@@ -281,25 +264,23 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SunoApi getOrCreateSunoApi(String apiKey, String url) {
|
||||
public SunoApi getOrCreateSunoApi(String rawApiKey, String rawUrl) {
|
||||
final String apiKey = resolveSpringPlaceholders(rawApiKey);
|
||||
final String url = resolveSpringPlaceholders(rawUrl);
|
||||
String cacheKey = buildClientCacheKey(SunoApi.class, AiPlatformEnum.SUNO.getPlatform(), apiKey, url);
|
||||
return Singleton.get(cacheKey, (Func0<SunoApi>) () -> new SunoApi(url));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("EnhancedSwitchMigration")
|
||||
public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model) {
|
||||
public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String rawApiKey, String rawUrl, String model) {
|
||||
final String apiKey = resolveSpringPlaceholders(rawApiKey);
|
||||
final String url = resolveSpringPlaceholders(rawUrl);
|
||||
String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url, model);
|
||||
return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> {
|
||||
switch (platform) {
|
||||
case TONG_YI:
|
||||
return buildTongYiEmbeddingModel(apiKey, model);
|
||||
case YI_YAN:
|
||||
return buildYiYanEmbeddingModel(apiKey, model);
|
||||
case ZHI_PU:
|
||||
return buildZhiPuEmbeddingModel(apiKey, url, model);
|
||||
case MINI_MAX:
|
||||
return buildMiniMaxEmbeddingModel(apiKey, url, model);
|
||||
case OPENAI:
|
||||
return buildOpenAiEmbeddingModel(apiKey, url, model);
|
||||
case AZURE_OPENAI:
|
||||
@@ -341,6 +322,11 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));
|
||||
}
|
||||
|
||||
private static String resolveSpringPlaceholders(String value) {
|
||||
// yml 配置的占位符由 Spring 自动解析;DB 里保存的 ${xxx} 需要在这里手动解析。
|
||||
return AiUtils.resolveSpringPlaceholders(value);
|
||||
}
|
||||
|
||||
// ========== 各种创建 spring-ai 客户端的方法 ==========
|
||||
|
||||
/**
|
||||
@@ -367,30 +353,10 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link QianFanChatAutoConfiguration} 的 qianFanChatModel 方法
|
||||
*/
|
||||
private static QianFanChatModel buildYiYanChatModel(String key) {
|
||||
// TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6
|
||||
List<String> keys = StrUtil.split(key, '|');
|
||||
Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
|
||||
String appKey = keys.get(0);
|
||||
String secretKey = keys.get(1);
|
||||
QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);
|
||||
return new QianFanChatModel(qianFanApi);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanImageModel 方法
|
||||
*/
|
||||
private QianFanImageModel buildQianFanImageModel(String key) {
|
||||
// TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6
|
||||
List<String> keys = StrUtil.split(key, '|');
|
||||
Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
|
||||
String appKey = keys.get(0);
|
||||
String secretKey = keys.get(1);
|
||||
QianFanImageApi qianFanApi = new QianFanImageApi(appKey, secretKey);
|
||||
return new QianFanImageModel(qianFanApi);
|
||||
private ChatModel buildYiYanChatModel(String apiKey) {
|
||||
YudaoAiProperties.YiYan properties = new YudaoAiProperties.YiYan()
|
||||
.setApiKey(apiKey);
|
||||
return new AiAutoConfiguration().buildYiYanChatClient(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -435,62 +401,47 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link ZhiPuAiChatAutoConfiguration} 的 zhiPuAiChatModel 方法
|
||||
* 可参考 {@link AiAutoConfiguration#zhiPuChatClient(YudaoAiProperties)}
|
||||
*/
|
||||
private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
|
||||
ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey);
|
||||
if (StrUtil.isNotEmpty(url)) {
|
||||
zhiPuAiApiBuilder.baseUrl(url);
|
||||
}
|
||||
ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();
|
||||
return new ZhiPuAiChatModel(zhiPuAiApiBuilder.build(), options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE,
|
||||
getObservationRegistry().getIfAvailable());
|
||||
private ZhiPuChatModel buildZhiPuChatModel(String apiKey, String url) {
|
||||
YudaoAiProperties.ZhiPu properties = new YudaoAiProperties.ZhiPu()
|
||||
.setBaseUrl(url).setApiKey(apiKey);
|
||||
return new AiAutoConfiguration().buildZhiPuChatClient(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link ZhiPuAiImageAutoConfiguration} 的 zhiPuAiImageModel 方法
|
||||
*/
|
||||
private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {
|
||||
ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey)
|
||||
: new ZhiPuAiImageApi(url, apiKey, RestClient.builder());
|
||||
return new ZhiPuAiImageModel(zhiPuAiApi);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link MiniMaxChatAutoConfiguration} 的 miniMaxChatModel 方法
|
||||
* 可参考 {@link AiAutoConfiguration#miniMaxChatClient(YudaoAiProperties)}
|
||||
*/
|
||||
private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) {
|
||||
MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey)
|
||||
: new MiniMaxApi(url, apiKey);
|
||||
MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();
|
||||
return new MiniMaxChatModel(miniMaxApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE);
|
||||
YudaoAiProperties.MiniMax properties = new YudaoAiProperties.MiniMax()
|
||||
.setBaseUrl(url).setApiKey(apiKey);
|
||||
return new AiAutoConfiguration().buildMiniMaxChatClient(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link MoonshotChatAutoConfiguration} 的 moonshotChatModel 方法
|
||||
* 可参考 {@link AiAutoConfiguration#moonshotChatClient(YudaoAiProperties)}
|
||||
*/
|
||||
private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) {
|
||||
MoonshotApi.Builder moonshotApiBuilder = MoonshotApi.builder()
|
||||
.apiKey(apiKey);
|
||||
if (StrUtil.isNotEmpty(url)) {
|
||||
moonshotApiBuilder.baseUrl(url);
|
||||
}
|
||||
MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build();
|
||||
return MoonshotChatModel.builder()
|
||||
.moonshotApi(moonshotApiBuilder.build())
|
||||
.defaultOptions(options)
|
||||
.toolCallingManager(getToolCallingManager())
|
||||
.build();
|
||||
YudaoAiProperties.Moonshot properties = new YudaoAiProperties.Moonshot()
|
||||
.setBaseUrl(url).setApiKey(apiKey);
|
||||
return new AiAutoConfiguration().buildMoonshotChatClient(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link AiAutoConfiguration#stepFunChatClient(YudaoAiProperties)}
|
||||
*/
|
||||
private StepFunChatModel buildStepFunChatModel(String apiKey, String url) {
|
||||
YudaoAiProperties.StepFun properties = new YudaoAiProperties.StepFun()
|
||||
.setBaseUrl(url).setApiKey(apiKey);
|
||||
return new AiAutoConfiguration().buildStepFunChatClient(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link AiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)}
|
||||
*/
|
||||
private static XingHuoChatModel buildXingHuoChatModel(String key) {
|
||||
List<String> keys = StrUtil.split(key, '|');
|
||||
Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式");
|
||||
private static XingHuoChatModel buildXingHuoChatModel(String apiKey) {
|
||||
YudaoAiProperties.XingHuo properties = new YudaoAiProperties.XingHuo()
|
||||
.setAppKey(keys.get(0)).setSecretKey(keys.get(1));
|
||||
.setApiKey(apiKey).setModel(XingHuoChatModel.MODEL_DEFAULT);
|
||||
return new AiAutoConfiguration().buildXingHuoChatClient(properties);
|
||||
}
|
||||
|
||||
@@ -590,7 +541,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return new StabilityAiImageModel(stabilityAiApi);
|
||||
}
|
||||
|
||||
private ChatModel buildGrokChatModel(String apiKey,String url) {
|
||||
private GrokChatModel buildGrokChatModel(String apiKey, String url) {
|
||||
YudaoAiProperties.Grok properties = new YudaoAiProperties.Grok()
|
||||
.setBaseUrl(url)
|
||||
.setApiKey(apiKey);
|
||||
@@ -608,41 +559,6 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link ZhiPuAiEmbeddingAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法
|
||||
*/
|
||||
private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) {
|
||||
ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey);
|
||||
if (StrUtil.isNotEmpty(url)) {
|
||||
zhiPuAiApiBuilder.baseUrl(url);
|
||||
}
|
||||
ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build();
|
||||
return new ZhiPuAiEmbeddingModel(zhiPuAiApiBuilder.build(), MetadataMode.EMBED, zhiPuAiEmbeddingOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link MiniMaxEmbeddingAutoConfiguration} 的 miniMaxEmbeddingModel 方法
|
||||
*/
|
||||
private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) {
|
||||
MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey)
|
||||
: new MiniMaxApi(url, apiKey);
|
||||
MiniMaxEmbeddingOptions miniMaxEmbeddingOptions = MiniMaxEmbeddingOptions.builder().model(model).build();
|
||||
return new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED, miniMaxEmbeddingOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanEmbeddingModel 方法
|
||||
*/
|
||||
private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) {
|
||||
List<String> keys = StrUtil.split(key, '|');
|
||||
Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
|
||||
String appKey = keys.get(0);
|
||||
String secretKey = keys.get(1);
|
||||
QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);
|
||||
QianFanEmbeddingOptions qianFanEmbeddingOptions = QianFanEmbeddingOptions.builder().model(model).build();
|
||||
return new QianFanEmbeddingModel(qianFanApi, MetadataMode.EMBED, qianFanEmbeddingOptions);
|
||||
}
|
||||
|
||||
private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) {
|
||||
OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build();
|
||||
OllamaEmbeddingOptions ollamaOptions = OllamaEmbeddingOptions.builder().model(model).build();
|
||||
|
||||
+9
-7
@@ -6,7 +6,7 @@ import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
@@ -20,26 +20,28 @@ public class BaiChuanChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://api.baichuan-ai.com";
|
||||
|
||||
public static final String MODEL_DEFAULT = "Baichuan4-Turbo";
|
||||
public static final String COMPLETE_PATH = "/v1/chat/completions";
|
||||
|
||||
public static final String MODEL_DEFAULT = "Baichuan-M3";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,进行复用
|
||||
* 兼容 OpenAI 接口,复用 DeepSeek 客户端
|
||||
*/
|
||||
private final OpenAiChatModel openAiChatModel;
|
||||
private final DeepSeekChatModel deepSeekChatModel;
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return openAiChatModel.call(prompt);
|
||||
return deepSeekChatModel.call(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return openAiChatModel.stream(prompt);
|
||||
return deepSeekChatModel.stream(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return openAiChatModel.getDefaultOptions();
|
||||
return deepSeekChatModel.getDefaultOptions();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ public class DouBaoChatModel implements ChatModel {
|
||||
public static final String BASE_URL = "https://ark.cn-beijing.volces.com/api";
|
||||
public static final String COMPLETE_PATH = "/v3/chat/completions";
|
||||
|
||||
public static final String MODEL_DEFAULT = "doubao-1-5-lite-32k-250115";
|
||||
public static final String MODEL_DEFAULT = "doubao-seed-2-1-turbo-260628";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,进行复用
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ public class GrokChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://api.x.ai";
|
||||
public static final String COMPLETE_PATH = "/v1/chat/completions";
|
||||
public static final String MODEL_DEFAULT = "grok-4-fast-reasoning";
|
||||
public static final String MODEL_DEFAULT = "grok-4.3";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,进行复用
|
||||
|
||||
+4
-9
@@ -9,10 +9,9 @@ import org.springframework.ai.chat.prompt.Prompt;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* 腾云混元 {@link ChatModel} 实现类
|
||||
* 腾讯混元 {@link ChatModel} 实现类
|
||||
*
|
||||
* 1. 混元大模型:基于 <a href="https://cloud.tencent.com/document/product/1729/111007">知识引擎原子能力</a> 实现
|
||||
* 2. 知识引擎原子能力:基于 <a href="https://cloud.tencent.com/document/product/1772/115969">知识引擎原子能力</a> 实现
|
||||
* 基于 <a href="https://cloud.tencent.com/document/product/1823/132252">TokenHub OpenAI 兼容接口</a> 实现
|
||||
*
|
||||
* @author fansili
|
||||
*/
|
||||
@@ -20,14 +19,10 @@ import reactor.core.publisher.Flux;
|
||||
@RequiredArgsConstructor
|
||||
public class HunYuanChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://api.hunyuan.cloud.tencent.com";
|
||||
public static final String BASE_URL = "https://tokenhub.tencentmaas.com";
|
||||
public static final String COMPLETE_PATH = "/v1/chat/completions";
|
||||
|
||||
public static final String MODEL_DEFAULT = "hunyuan-turbo";
|
||||
|
||||
public static final String DEEP_SEEK_BASE_URL = "https://api.lkeap.cloud.tencent.com";
|
||||
|
||||
public static final String DEEP_SEEK_MODEL_DEFAULT = "deepseek-v3";
|
||||
public static final String MODEL_DEFAULT = "hy3-preview";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,进行复用
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* MiniMax {@link ChatModel} 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class MiniMaxChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://api.minimaxi.com/v1";
|
||||
|
||||
public static final String MODEL_DEFAULT = "MiniMax-M3";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,复用 DeepSeek 客户端
|
||||
*/
|
||||
private final DeepSeekChatModel deepSeekChatModel;
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return deepSeekChatModel.call(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return deepSeekChatModel.stream(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return deepSeekChatModel.getDefaultOptions();
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* 月之暗面 {@link ChatModel} 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class MoonshotChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://api.moonshot.cn";
|
||||
|
||||
public static final String COMPLETE_PATH = "/v1/chat/completions";
|
||||
|
||||
public static final String MODEL_DEFAULT = "kimi-k2.6";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,复用 DeepSeek 客户端
|
||||
*/
|
||||
private final DeepSeekChatModel deepSeekChatModel;
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return deepSeekChatModel.call(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return deepSeekChatModel.stream(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return deepSeekChatModel.getDefaultOptions();
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* 阶跃星辰 {@link ChatModel} 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class StepFunChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://api.stepfun.com";
|
||||
|
||||
public static final String COMPLETE_PATH = "/v1/chat/completions";
|
||||
|
||||
public static final String MODEL_DEFAULT = "step-3.7-flash";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,复用 DeepSeek 客户端
|
||||
*/
|
||||
private final DeepSeekChatModel deepSeekChatModel;
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return deepSeekChatModel.call(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return deepSeekChatModel.stream(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return deepSeekChatModel.getDefaultOptions();
|
||||
}
|
||||
|
||||
}
|
||||
+119
-13
@@ -1,11 +1,14 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
@@ -14,37 +17,140 @@ import reactor.core.publisher.Flux;
|
||||
* @author fansili
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class XingHuoChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL_V1 = "https://spark-api-open.xf-yun.com";
|
||||
|
||||
public static final String BASE_URL_V2 = "https://spark-api-open.xf-yun.com";
|
||||
public static final String BASE_COMPLETIONS_PATH_V2 = "/v2/chat/completions";
|
||||
/**
|
||||
* 星火 X2
|
||||
*
|
||||
* @see <a href="https://spark-api-open.xf-yun.com/x2/chat/completions">接口地址</a>
|
||||
*/
|
||||
public static final String MODEL_X2 = "x2";
|
||||
|
||||
/**
|
||||
* 已知模型名列表:x1、4.0Ultra、generalv3.5、max-32k、generalv3、pro-128k、lite
|
||||
* 星火 X2 Flash
|
||||
*
|
||||
* @see <a href="https://spark-api-open.xf-yun.com/agent/v1/chat/completions">接口地址</a>
|
||||
*/
|
||||
public static final String MODEL_DEFAULT = "4.0Ultra";
|
||||
public static final String MODEL_X2_FLASH = "x2-flash";
|
||||
|
||||
private static final String BASE_URL_X2 = "https://spark-api-open.xf-yun.com/x2";
|
||||
|
||||
private static final String BASE_URL_X2_FLASH = "https://spark-api-open.xf-yun.com/agent/v1";
|
||||
|
||||
public static final String MODEL_DEFAULT = MODEL_X2_FLASH;
|
||||
|
||||
private static String getBaseUrl(String model) {
|
||||
if (MODEL_X2_FLASH.equals(model)) {
|
||||
return BASE_URL_X2_FLASH;
|
||||
}
|
||||
return BASE_URL_X2;
|
||||
}
|
||||
|
||||
private final String apiKey;
|
||||
|
||||
private final DeepSeekChatOptions options;
|
||||
|
||||
/**
|
||||
* v1 兼容 OpenAI 接口,进行复用
|
||||
* 兼容 OpenAI 接口,进行复用
|
||||
*/
|
||||
private final ChatModel openAiChatModelV1;
|
||||
private final ChatModel chatModelX2;
|
||||
|
||||
private final ChatModel chatModelX2Flash;
|
||||
|
||||
private XingHuoChatModel(String apiKey, DeepSeekChatOptions options) {
|
||||
this.apiKey = apiKey;
|
||||
this.options = options;
|
||||
this.chatModelX2 = buildChatModel(MODEL_X2);
|
||||
this.chatModelX2Flash = buildChatModel(MODEL_X2_FLASH);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return openAiChatModelV1.call(prompt);
|
||||
return getChatModel(prompt).call(buildApiPrompt(prompt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return openAiChatModelV1.stream(prompt);
|
||||
return getChatModel(prompt).stream(buildApiPrompt(prompt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return openAiChatModelV1.getDefaultOptions();
|
||||
return options;
|
||||
}
|
||||
|
||||
private ChatModel getChatModel(Prompt prompt) {
|
||||
String model = options.getModel();
|
||||
ChatOptions options = prompt.getOptions();
|
||||
if (options != null && isBusinessModel(options.getModel())) {
|
||||
model = options.getModel();
|
||||
}
|
||||
return getChatModel(model);
|
||||
}
|
||||
|
||||
private ChatModel getChatModel(String model) {
|
||||
if (MODEL_X2_FLASH.equals(model)) {
|
||||
return chatModelX2Flash;
|
||||
}
|
||||
return chatModelX2;
|
||||
}
|
||||
|
||||
private ChatModel buildChatModel(String model) {
|
||||
return DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(getBaseUrl(model))
|
||||
.apiKey(apiKey)
|
||||
.build())
|
||||
.defaultOptions(new DeepSeekChatOptions.Builder(options).model("spark-x").build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Prompt buildApiPrompt(Prompt prompt) {
|
||||
ChatOptions options = prompt.getOptions();
|
||||
if (options == null) {
|
||||
return prompt;
|
||||
}
|
||||
if (!(options instanceof DeepSeekChatOptions)) {
|
||||
return prompt;
|
||||
}
|
||||
return Prompt.builder()
|
||||
.messages(prompt.getInstructions())
|
||||
.chatOptions(new DeepSeekChatOptions.Builder((DeepSeekChatOptions) options).model("spark-x").build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static boolean isBusinessModel(String model) {
|
||||
return MODEL_X2.equals(model) || MODEL_X2_FLASH.equals(model);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
||||
private String apiKey;
|
||||
|
||||
private DeepSeekChatOptions options;
|
||||
|
||||
public Builder apiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder options(DeepSeekChatOptions options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
public XingHuoChatModel build() {
|
||||
DeepSeekChatOptions options = this.options != null ? this.options : DeepSeekChatOptions.builder().build();
|
||||
if (StrUtil.isEmpty(options.getModel())) {
|
||||
options = new DeepSeekChatOptions.Builder(options).model(MODEL_DEFAULT).build();
|
||||
}
|
||||
return new XingHuoChatModel(apiKey, options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* 文心一言 {@link ChatModel} 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class YiYanChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://qianfan.baidubce.com/v2";
|
||||
|
||||
public static final String MODEL_DEFAULT = "ernie-5.1";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,复用 DeepSeek 客户端
|
||||
*/
|
||||
private final DeepSeekChatModel deepSeekChatModel;
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return deepSeekChatModel.call(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return deepSeekChatModel.stream(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return deepSeekChatModel.getDefaultOptions();
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* 智谱 {@link ChatModel} 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class ZhiPuChatModel implements ChatModel {
|
||||
|
||||
public static final String BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
|
||||
|
||||
public static final String MODEL_DEFAULT = "glm-5.2";
|
||||
|
||||
/**
|
||||
* 兼容 OpenAI 接口,复用 DeepSeek 客户端
|
||||
*/
|
||||
private final DeepSeekChatModel deepSeekChatModel;
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return deepSeekChatModel.call(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return deepSeekChatModel.stream(prompt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatOptions getDefaultOptions() {
|
||||
return deepSeekChatModel.getDefaultOptions();
|
||||
}
|
||||
|
||||
}
|
||||
-11
@@ -29,14 +29,12 @@ import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springaicommunity.qianfan.QianFanImageOptions;
|
||||
import org.springframework.ai.image.ImageModel;
|
||||
import org.springframework.ai.image.ImageOptions;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.image.ImageResponse;
|
||||
import org.springframework.ai.openai.OpenAiImageOptions;
|
||||
import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -168,15 +166,6 @@ public class AiImageServiceImpl implements AiImageService {
|
||||
.model(model.getModel()).n(1)
|
||||
.height(draw.getHeight()).width(draw.getWidth())
|
||||
.build();
|
||||
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
|
||||
return QianFanImageOptions.builder()
|
||||
.model(model.getModel()).N(1)
|
||||
.height(draw.getHeight()).width(draw.getWidth())
|
||||
.build();
|
||||
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
|
||||
return ZhiPuAiImageOptions.builder()
|
||||
.model(model.getModel())
|
||||
.build();
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform());
|
||||
}
|
||||
|
||||
+42
-18
@@ -8,8 +8,7 @@ import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
|
||||
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
|
||||
import org.springaicommunity.moonshot.MoonshotChatOptions;
|
||||
import org.springaicommunity.qianfan.QianFanChatOptions;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import org.springframework.ai.anthropic.AnthropicChatOptions;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
|
||||
import org.springframework.ai.chat.messages.*;
|
||||
@@ -17,14 +16,16 @@ import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.deepseek.DeepSeekAssistantMessage;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.ollama.api.OllamaChatOptions;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.tool.ToolCallback;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_CONFIG_PLACEHOLDER_NOT_RESOLVED;
|
||||
|
||||
/**
|
||||
* Spring AI 工具类
|
||||
*
|
||||
@@ -35,6 +36,34 @@ public class AiUtils {
|
||||
public static final String TOOL_CONTEXT_LOGIN_USER = "LOGIN_USER";
|
||||
public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID";
|
||||
|
||||
/**
|
||||
* 解析 DB 等动态配置里的 Spring 占位符,例如 ${OPENAI_API_KEY}
|
||||
*
|
||||
* @param value 待解析的配置值
|
||||
* @return 解析后的配置值
|
||||
*/
|
||||
public static String resolveSpringPlaceholders(String value) {
|
||||
if (StrUtil.isBlank(value) || !StrUtil.contains(value, "${")) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return SpringUtil.getBean(Environment.class).resolveRequiredPlaceholders(value);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw exception(API_CONFIG_PLACEHOLDER_NOT_RESOLVED, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 API Key,避免集成测试使用默认占位值发起调用。
|
||||
*
|
||||
* @param apiKey API Key
|
||||
*/
|
||||
public static void validateApiKey(String apiKey) {
|
||||
if (StrUtil.isBlank(apiKey) || "sk-xxxx".equals(apiKey)) {
|
||||
throw new IllegalStateException("apiKey 不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通义千问支持多模态的模型
|
||||
*
|
||||
@@ -42,7 +71,8 @@ public class AiUtils {
|
||||
* @see <a href="https://help.aliyun.com/zh/model-studio/error-code#error-url">必须开启 withMultiModel 参数</a>
|
||||
*/
|
||||
public static final Set<String> TONG_YI_MULTI_MODELS = SetUtils.asSet(
|
||||
// qwen3.5 / 3.6 系列(统一多模态主干)
|
||||
// qwen3.5 / 3.6 / 3.7 系列(统一多模态主干)
|
||||
"qwen3.7-max", "qwen3.7-plus", "qwen3.7-flash",
|
||||
"qwen3.6-plus", "qwen3.6-flash",
|
||||
"qwen3.5-plus", "qwen3.5-flash",
|
||||
// qwen-vl 视觉理解
|
||||
@@ -72,27 +102,21 @@ public class AiUtils {
|
||||
.enableThinking(true) // TODO 芋艿:默认都开启 thinking 模式,后续可以让用户配置
|
||||
.multiModel(TONG_YI_MULTI_MODELS.contains(model)) // 是否多模态模型
|
||||
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
|
||||
case YI_YAN:
|
||||
return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();
|
||||
case DEEP_SEEK:
|
||||
case DOU_BAO: // 复用 DeepSeek 客户端
|
||||
case HUN_YUAN: // 复用 DeepSeek 客户端
|
||||
case SILICON_FLOW: // 复用 DeepSeek 客户端
|
||||
case YI_YAN: // 复用 DeepSeek 客户端
|
||||
case ZHI_PU: // 复用 DeepSeek 客户端
|
||||
case XING_HUO: // 复用 DeepSeek 客户端
|
||||
case MINI_MAX: // 复用 DeepSeek 客户端
|
||||
case MOONSHOT: // 复用 DeepSeek 客户端
|
||||
case BAI_CHUAN: // 复用 DeepSeek 客户端
|
||||
case STEP_FUN: // 复用 DeepSeek 客户端
|
||||
return DeepSeekChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
|
||||
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
|
||||
case ZHI_PU:
|
||||
return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
|
||||
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
|
||||
case MINI_MAX:
|
||||
return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
|
||||
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
|
||||
case MOONSHOT:
|
||||
return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
|
||||
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
|
||||
case OPENAI:
|
||||
case GEMINI: // 复用 OpenAI 客户端
|
||||
case BAI_CHUAN: // 复用 OpenAI 客户端
|
||||
case GROK: // 复用 OpenAI 客户端
|
||||
return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
|
||||
.toolCallbacks(toolCallbacks).toolContext(toolContext).build();
|
||||
@@ -159,4 +183,4 @@ public class AiUtils {
|
||||
return MapUtil.getStr(output.getMetadata(), "reasoningContent");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,36 +121,28 @@ spring:
|
||||
client:
|
||||
host: 127.0.0.1
|
||||
port: 19530
|
||||
qianfan: # 文心一言
|
||||
api-key: x0cuLZ7XsaTCU08vuJWO87Lg
|
||||
secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK
|
||||
zhipuai: # 智谱 AI
|
||||
api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs
|
||||
openai: # OpenAI 官方
|
||||
api-key: sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17
|
||||
base-url: https://api.gptsapi.net
|
||||
base-url: ${OPENAI_BASE_URL:https://api.openai.com}
|
||||
api-key: ${OPENAI_API_KEY:sk-xxxx}
|
||||
azure: # OpenAI 微软
|
||||
openai:
|
||||
endpoint: https://eastusprejade.openai.azure.com
|
||||
anthropic: # Anthropic Claude
|
||||
api-key: sk-muubv7cXeLw0Etgs743f365cD5Ea44429946Fa7e672d8942
|
||||
base-url: ${ANTHROPIC_BASE_URL:https://api.anthropic.com}
|
||||
api-key: ${ANTHROPIC_API_KEY:sk-xxxx}
|
||||
ollama:
|
||||
base-url: http://127.0.0.1:11434
|
||||
chat:
|
||||
model: llama3
|
||||
stabilityai:
|
||||
api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx
|
||||
api-key: ${STABILITYAI_API_KEY:sk-xxxx}
|
||||
dashscope: # 通义千问
|
||||
api-key: sk-47aa124781be4bfb95244cc62f6xxxx
|
||||
minimax: # Minimax:https://www.minimaxi.com/
|
||||
api-key: xxxx
|
||||
moonshot: # 月之暗面(KIMI)
|
||||
api-key: sk-abc
|
||||
api-key: ${DASHSCOPE_API_KEY:sk-xxxx}
|
||||
deepseek: # DeepSeek
|
||||
api-key: sk-e94db327cc7d457d99a8de8810fc6b12
|
||||
api-key: ${DEEPSEEK_API_KEY:sk-xxxx}
|
||||
chat:
|
||||
options:
|
||||
model: deepseek-chat
|
||||
model: deepseek-v4-flash
|
||||
model:
|
||||
rerank: false # 是否开启“通义千问”的 Rerank 模型,填写 dashscope 开启
|
||||
mcp:
|
||||
@@ -175,34 +167,57 @@ yudao:
|
||||
ai:
|
||||
gemini: # 谷歌 Gemini
|
||||
enable: true
|
||||
api-key: AIzaSyAVoBxgoFvvte820vEQMma2LKBnC98bqMQ
|
||||
api-key: ${GEMINI_API_KEY:sk-xxxx}
|
||||
model: gemini-2.5-flash
|
||||
doubao: # 字节豆包
|
||||
enable: true
|
||||
api-key: 5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272
|
||||
model: doubao-1-5-lite-32k-250115
|
||||
api-key: ${DOUBAO_API_KEY:sk-xxxx}
|
||||
model: doubao-seed-2-1-turbo-260628
|
||||
hunyuan: # 腾讯混元
|
||||
enable: true
|
||||
api-key: sk-abc
|
||||
model: hunyuan-turbo
|
||||
api-key: ${HUNYUAN_API_KEY:sk-xxxx}
|
||||
model: hy3-preview
|
||||
siliconflow: # 硅基流动
|
||||
enable: true
|
||||
api-key: sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
|
||||
api-key: ${SILICONFLOW_API_KEY:sk-xxxx}
|
||||
model: deepseek-ai/DeepSeek-V4-Pro
|
||||
xinghuo: # 讯飞星火
|
||||
enable: true
|
||||
appKey: 75b161ed2aef4719b275d6e7f2a4d4cd
|
||||
secretKey: YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz
|
||||
model: x1
|
||||
api-key: ${XINGHUO_API_KEY:sk-xxxx}
|
||||
model: x2-flash
|
||||
baichuan: # 百川智能
|
||||
enable: true
|
||||
api-key: sk-abc
|
||||
model: Baichuan4-Turbo
|
||||
api-key: ${BAICHUAN_API_KEY:sk-xxxx}
|
||||
model: Baichuan-M3
|
||||
yiyan: # 文心一言
|
||||
enable: false
|
||||
api-key: ${YIYAN_API_KEY:sk-xxxx}
|
||||
model: ernie-5.1
|
||||
zhipu: # 智谱 AI(GLM)
|
||||
enable: false
|
||||
api-key: ${ZHIPU_API_KEY:sk-xxxx}
|
||||
model: glm-5.2
|
||||
minimax: # MiniMax
|
||||
enable: false
|
||||
api-key: ${MINIMAX_API_KEY:sk-xxxx}
|
||||
model: MiniMax-M3
|
||||
moonshot: # 月之暗面(KIMI)
|
||||
enable: false
|
||||
api-key: ${MOONSHOT_API_KEY:sk-xxxx}
|
||||
model: kimi-k2.6
|
||||
stepfun: # 阶跃星辰
|
||||
enable: false
|
||||
api-key: ${STEPFUN_API_KEY:sk-xxxx}
|
||||
model: step-3.7-flash
|
||||
grok: # Grok
|
||||
enable: false
|
||||
api-key: ${GROK_API_KEY:sk-xxxx}
|
||||
model: grok-4.3
|
||||
midjourney:
|
||||
enable: true
|
||||
# base-url: https://api.holdai.top/mj-relax/mj
|
||||
base-url: https://api.holdai.top/mj
|
||||
api-key: sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf
|
||||
api-key: ${MIDJOURNEY_API_KEY:sk-xxxx}
|
||||
notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify
|
||||
suno:
|
||||
enable: true
|
||||
@@ -210,7 +225,7 @@ yudao:
|
||||
base-url: http://127.0.0.1:3001
|
||||
web-search:
|
||||
enable: true
|
||||
api-key: sk-40500e52840f4d24b956d0b1d80d9abe
|
||||
api-key: ${WEB_SEARCH_API_KEY:sk-xxxx}
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
|
||||
+26
-12
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -8,13 +9,16 @@ import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link BaiChuanChatModel} 集成测试
|
||||
@@ -23,22 +27,27 @@ import java.util.List;
|
||||
*/
|
||||
public class BaiChuanChatModelTests {
|
||||
|
||||
private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||
.openAiApi(OpenAiApi.builder()
|
||||
private static final String API_KEY = SystemUtil.get("BAICHUAN_API_KEY",
|
||||
"sk-xxxx"); // 按需改成你的百川 API Key
|
||||
private static final String MODEL = SystemUtil.get("BAICHUAN_MODEL",
|
||||
BaiChuanChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final BaiChuanChatModel chatModel = new BaiChuanChatModel(DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(BaiChuanChatModel.BASE_URL)
|
||||
.apiKey("sk-61b6766a94c70786ed02673f5e16af3c") // apiKey
|
||||
.completionsPath(BaiChuanChatModel.COMPLETE_PATH)
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(OpenAiChatOptions.builder()
|
||||
.model("Baichuan4-Turbo") // 模型(https://platform.baichuan-ai.com/docs/api)
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
private final BaiChuanChatModel chatModel = new BaiChuanChatModel(openAiChatModel);
|
||||
.build());
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -48,11 +57,13 @@ public class BaiChuanChatModelTests {
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -61,7 +72,10 @@ public class BaiChuanChatModelTests {
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
|
||||
// 打印结果
|
||||
flux.doOnNext(System.out::println).then().block();
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-4
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
@@ -14,6 +15,9 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link DeepSeekChatModel} 集成测试
|
||||
@@ -22,12 +26,17 @@ import java.util.List;
|
||||
*/
|
||||
public class DeepSeekChatModelTests {
|
||||
|
||||
private static final String API_KEY = SystemUtil.get("DEEPSEEK_API_KEY",
|
||||
"sk-xxxx");
|
||||
private static final String MODEL = SystemUtil.get("DEEPSEEK_MODEL",
|
||||
"deepseek-v4-flash");
|
||||
|
||||
private final DeepSeekChatModel chatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.apiKey("sk-eaf4172a057344dd9bc64b1f806b6axx") // apiKey
|
||||
.apiKey(API_KEY) // apiKey
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model("deepseek-chat") // 模型
|
||||
.model(MODEL) // 模型
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build();
|
||||
@@ -35,6 +44,7 @@ public class DeepSeekChatModelTests {
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -43,12 +53,13 @@ public class DeepSeekChatModelTests {
|
||||
// 调用
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -63,11 +74,12 @@ public class DeepSeekChatModelTests {
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_thinking() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model("deepseek-reasoner")
|
||||
.model(MODEL)
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
|
||||
+21
-13
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -15,6 +16,9 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link DouBaoChatModel} 集成测试
|
||||
@@ -23,19 +27,19 @@ import java.util.List;
|
||||
*/
|
||||
public class DouBaoChatModelTests {
|
||||
|
||||
/**
|
||||
* 相比 OpenAIChatModel 来说,DeepSeekChatModel 可以兼容豆包的 thinking 能力!
|
||||
*/
|
||||
private static final String API_KEY = SystemUtil.get("DOUBAO_API_KEY",
|
||||
"sk-xxxx"); // 按需改成你的豆包 API Key
|
||||
private static final String MODEL = SystemUtil.get("DOUBAO_MODEL",
|
||||
DouBaoChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(DouBaoChatModel.BASE_URL)
|
||||
.completionsPath(DouBaoChatModel.COMPLETE_PATH)
|
||||
.apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model("doubao-1-5-lite-32k-250115") // 模型(doubao)
|
||||
// .model("doubao-seed-1-6-thinking-250715") // 模型(doubao)
|
||||
// .model("deepseek-r1-250120") // 模型(deepseek)
|
||||
.model(MODEL)
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build();
|
||||
@@ -45,6 +49,7 @@ public class DouBaoChatModelTests {
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -54,11 +59,13 @@ public class DouBaoChatModelTests {
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -67,25 +74,26 @@ public class DouBaoChatModelTests {
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
|
||||
// 打印结果
|
||||
flux.doOnNext(System.out::println).then().block();
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_thinking() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model("doubao-seed-1-6-thinking-250715")
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
|
||||
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.openai.OpenAiChatModel;
|
||||
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link GrokChatModel} 集成测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class GrokChatModelTests {
|
||||
|
||||
private static final String API_KEY = SystemUtil.get("GROK_API_KEY",
|
||||
"sk-xxxx");
|
||||
private static final String MODEL = SystemUtil.get("GROK_MODEL",
|
||||
GrokChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final GrokChatModel chatModel = new GrokChatModel(OpenAiChatModel.builder()
|
||||
.openAiApi(OpenAiApi.builder()
|
||||
.baseUrl(GrokChatModel.BASE_URL)
|
||||
.completionsPath(GrokChatModel.COMPLETE_PATH)
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(OpenAiChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
}
|
||||
+16
-67
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -15,6 +16,9 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link HunYuanChatModel} 集成测试
|
||||
@@ -23,14 +27,19 @@ import java.util.List;
|
||||
*/
|
||||
public class HunYuanChatModelTests {
|
||||
|
||||
private static final String API_KEY = SystemUtil.get("HUNYUAN_API_KEY",
|
||||
"sk-xxxx");
|
||||
private static final String MODEL = SystemUtil.get("HUNYUAN_MODEL",
|
||||
HunYuanChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(HunYuanChatModel.BASE_URL)
|
||||
.completionsPath(HunYuanChatModel.COMPLETE_PATH)
|
||||
.apiKey("sk-abc") // apiKey
|
||||
.apiKey(API_KEY) // apiKey
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(HunYuanChatModel.MODEL_DEFAULT) // 模型
|
||||
.model(MODEL) // 模型
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build();
|
||||
@@ -40,6 +49,7 @@ public class HunYuanChatModelTests {
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -48,12 +58,13 @@ public class HunYuanChatModelTests {
|
||||
// 调用
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -68,12 +79,12 @@ public class HunYuanChatModelTests {
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_thinking() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model("hunyuan-a13b")
|
||||
// .model("hunyuan-turbos-latest")
|
||||
.model(MODEL)
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
@@ -85,66 +96,4 @@ public class HunYuanChatModelTests {
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
private final DeepSeekChatModel deepSeekOpenAiChatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(HunYuanChatModel.DEEP_SEEK_BASE_URL)
|
||||
.completionsPath(HunYuanChatModel.COMPLETE_PATH)
|
||||
.apiKey("sk-abc") // apiKey
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
// .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // 模型("deepseek-v3")
|
||||
.model("deepseek-r1") // 模型("deepseek-r1")
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
private final HunYuanChatModel deepSeekChatModel = new HunYuanChatModel(deepSeekOpenAiChatModel);
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall_deepseek() {
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
ChatResponse response = deepSeekChatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_deepseek() {
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages));
|
||||
// 打印结果
|
||||
flux.doOnNext(System.out::println).then().block();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_deepseek_thinking() {
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model("deepseek-r1")
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages, options));
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-14
@@ -1,5 +1,7 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
@@ -7,13 +9,16 @@ import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.minimax.MiniMaxChatModel;
|
||||
import org.springframework.ai.minimax.MiniMaxChatOptions;
|
||||
import org.springframework.ai.minimax.api.MiniMaxApi;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link MiniMaxChatModel} 的集成测试
|
||||
@@ -22,14 +27,24 @@ import java.util.List;
|
||||
*/
|
||||
public class MiniMaxChatModelTests {
|
||||
|
||||
private final MiniMaxChatModel chatModel = new MiniMaxChatModel(
|
||||
new MiniMaxApi("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLnjovmlofmlowiLCJVc2VyTmFtZSI6IueOi-aWh-aWjCIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxODk3Mjg3MjQ5NDU2ODA4MzQ2IiwiUGhvbmUiOiIxNTYwMTY5MTM5OSIsIkdyb3VwSUQiOiIxODk3Mjg3MjQ5NDQ4NDE5NzM4IiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDMtMTEgMTI6NTI6MDIiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.aAuB7gWW_oA4IYhh-CF7c9MfWWxKN49B_HK-DYjXaDwwffhiG-H1571z1WQhp9QytWG-DqgLejneeSxkiq1wQIe3FsEP2wz4BmGBct31LehbJu8ehLxg_vg75Uod1nFAHbm5mZz6JSVLNIlSo87Xr3UtSzJhAXlapEkcqlA4YOzOpKrZ8l5_OJPTORTCmHWZYgJcRS-faNiH62ZnUEHUozesTFhubJHo5GfJCw_edlnmfSUocERV1BjWvenhZ9My-aYXNktcW9WaSj9l6gayV7A0Ium_PL55T9ln1PcI8gayiVUKJGJDoqNyF1AF9_aF9NOKtTnQzwNqnZdlTYH6hw"), // 密钥
|
||||
MiniMaxChatOptions.builder()
|
||||
.model(MiniMaxApi.ChatModel.ABAB_6_5_G_Chat.getValue()) // 模型
|
||||
.build());
|
||||
private static final String API_KEY = SystemUtil.get("MINIMAX_API_KEY",
|
||||
"sk-xxxx"); // 按需改成你的 MiniMax API Key
|
||||
private static final String MODEL = SystemUtil.get("MINIMAX_MODEL",
|
||||
MiniMaxChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final MiniMaxChatModel chatModel = new MiniMaxChatModel(DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(MiniMaxChatModel.BASE_URL)
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.build())
|
||||
.build());
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -39,12 +54,13 @@ public class MiniMaxChatModelTests {
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -55,19 +71,19 @@ public class MiniMaxChatModelTests {
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
// TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_thinking() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
MiniMaxChatOptions options = MiniMaxChatOptions.builder()
|
||||
.model("MiniMax-M1")
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
@@ -75,7 +91,7 @@ public class MiniMaxChatModelTests {
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
|
||||
+30
-16
@@ -1,19 +1,24 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springaicommunity.moonshot.MoonshotChatModel;
|
||||
import org.springaicommunity.moonshot.MoonshotChatOptions;
|
||||
import org.springaicommunity.moonshot.api.MoonshotApi;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link MoonshotChatModel} 的集成测试
|
||||
@@ -22,18 +27,27 @@ import java.util.List;
|
||||
*/
|
||||
public class MoonshotChatModelTests {
|
||||
|
||||
private final MoonshotChatModel chatModel = MoonshotChatModel.builder()
|
||||
.moonshotApi(MoonshotApi.builder()
|
||||
.apiKey("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA") // 密钥
|
||||
private static final String API_KEY = SystemUtil.get("MOONSHOT_API_KEY",
|
||||
"sk-xxxx"); // 按需改成你的 Moonshot API Key
|
||||
private static final String MODEL = SystemUtil.get("MOONSHOT_MODEL",
|
||||
MoonshotChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final MoonshotChatModel chatModel = new MoonshotChatModel(DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(MoonshotChatModel.BASE_URL)
|
||||
.completionsPath(MoonshotChatModel.COMPLETE_PATH)
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(MoonshotChatOptions.builder()
|
||||
.model("kimi-k2-0711-preview") // 模型
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.temperature(1D)
|
||||
.build())
|
||||
.build();
|
||||
.build());
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -43,12 +57,13 @@ public class MoonshotChatModelTests {
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -59,20 +74,19 @@ public class MoonshotChatModelTests {
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
// TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_thinking() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
MoonshotChatOptions options = MoonshotChatOptions.builder()
|
||||
// .model("kimi-k2-0711-preview")
|
||||
.model("kimi-thinking-preview")
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
@@ -80,7 +94,7 @@ public class MoonshotChatModelTests {
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link StepFunChatModel} 集成测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class StepFunChatModelTests {
|
||||
|
||||
private static final String API_KEY = SystemUtil.get("STEPFUN_API_KEY",
|
||||
"sk-xxxx");
|
||||
private static final String MODEL = SystemUtil.get("STEPFUN_MODEL",
|
||||
StepFunChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final StepFunChatModel chatModel = new StepFunChatModel(DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(StepFunChatModel.BASE_URL)
|
||||
.completionsPath(StepFunChatModel.COMPLETE_PATH)
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
// System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
System.out.println(response.getResult() != null ? response.getResult().getOutput() : null);
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
}
|
||||
+20
-17
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -8,13 +9,14 @@ import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link XingHuoChatModel} 集成测试
|
||||
@@ -23,24 +25,23 @@ import java.util.List;
|
||||
*/
|
||||
public class XingHuoChatModelTests {
|
||||
|
||||
private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(XingHuoChatModel.BASE_URL_V2)
|
||||
.completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2)
|
||||
.apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
// .model("generalv3.5") // 模型
|
||||
.model("x1") // 模型
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build();
|
||||
private static final String API_KEY = SystemUtil.get("XINGHUO_API_KEY",
|
||||
"sk-xxxx"); // 按需改成你的讯飞星火 API Key
|
||||
private static final String MODEL = SystemUtil.get("XINGHUO_MODEL",
|
||||
XingHuoChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final XingHuoChatModel chatModel = new XingHuoChatModel(openAiChatModel);
|
||||
private final XingHuoChatModel chatModel = XingHuoChatModel.builder()
|
||||
.apiKey(API_KEY)
|
||||
.options(DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -55,6 +56,7 @@ public class XingHuoChatModelTests {
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -69,11 +71,12 @@ public class XingHuoChatModelTests {
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_thinking() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model("x1")
|
||||
.model(MODEL)
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
@@ -81,7 +84,7 @@ public class XingHuoChatModelTests {
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
|
||||
+30
-16
@@ -1,62 +1,76 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springaicommunity.qianfan.QianFanChatModel;
|
||||
import org.springaicommunity.qianfan.QianFanChatOptions;
|
||||
import org.springaicommunity.qianfan.api.QianFanApi;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
// TODO @芋艿:百度千帆 API 提供了 V2 版本,目前 Spring AI 不兼容,可关键 <https://github.com/spring-projects/spring-ai/issues/2179> 进展
|
||||
/**
|
||||
* {@link QianFanChatModel} 的集成测试
|
||||
* {@link YiYanChatModel} 的集成测试
|
||||
*
|
||||
* @author fansili
|
||||
*/
|
||||
public class YiYanChatModelTests {
|
||||
|
||||
private final QianFanChatModel chatModel = new QianFanChatModel(
|
||||
new QianFanApi("DGnyzREuaY7av7c38bOM9Ji2", "9aR8myflEOPDrEeLhoXv0FdqANOAyIZW"), // 密钥
|
||||
QianFanChatOptions.builder()
|
||||
.model("ERNIE-4.5-8K-Preview")
|
||||
.build()
|
||||
);
|
||||
private static final String API_KEY = SystemUtil.get("YIYAN_API_KEY",
|
||||
"sk-xxxx"); // 按需改成你的文心一言 API Key
|
||||
private static final String MODEL = SystemUtil.get("YIYAN_MODEL",
|
||||
YiYanChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final YiYanChatModel chatModel = new YiYanChatModel(DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(YiYanChatModel.BASE_URL)
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
// TODO @芋艿:文心一言,只要带上 system message 就报错,已经各种测试,很莫名!
|
||||
// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
// TODO @芋艿:文心一言,只要带上 system message 就报错,已经各种测试,很莫名!
|
||||
// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
messages.add(new UserMessage("1 + 1 = ?"));
|
||||
|
||||
// 调用
|
||||
Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
|
||||
// 打印结果
|
||||
flux.doOnNext(System.out::println).then().block();
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-15
@@ -1,5 +1,7 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
@@ -7,31 +9,43 @@ import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
|
||||
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatModel;
|
||||
import org.springframework.ai.deepseek.DeepSeekChatOptions;
|
||||
import org.springframework.ai.deepseek.api.DeepSeekApi;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link ZhiPuAiChatModel} 的集成测试
|
||||
* {@link ZhiPuChatModel} 的集成测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class ZhiPuAiChatModelTests {
|
||||
|
||||
private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel(
|
||||
ZhiPuAiApi.builder().apiKey("2f35fb6ca4ea41fab898729b7fac086c.6ESSfPcCkxaKEUlR").build(), // 密钥
|
||||
ZhiPuAiChatOptions.builder()
|
||||
.model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // 模型
|
||||
private static final String API_KEY = SystemUtil.get("ZHIPU_API_KEY",
|
||||
"sk-xxxx"); // 按需改成你的智谱 API Key
|
||||
private static final String MODEL = SystemUtil.get("ZHIPU_MODEL",
|
||||
ZhiPuChatModel.MODEL_DEFAULT);
|
||||
|
||||
private final ZhiPuChatModel chatModel = new ZhiPuChatModel(DeepSeekChatModel.builder()
|
||||
.deepSeekApi(DeepSeekApi.builder()
|
||||
.baseUrl(ZhiPuChatModel.BASE_URL)
|
||||
.apiKey(API_KEY)
|
||||
.build())
|
||||
.defaultOptions(DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.build()
|
||||
);
|
||||
).build());
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -41,12 +55,13 @@ public class ZhiPuAiChatModelTests {
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
|
||||
@@ -57,19 +72,19 @@ public class ZhiPuAiChatModelTests {
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
// TODO @芋艿:暂时没解析 reasoning_content 结果,需要等官方修复
|
||||
@Test
|
||||
@Disabled
|
||||
public void testStream_thinking() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
List<Message> messages = new ArrayList<>();
|
||||
messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
|
||||
ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()
|
||||
.model("GLM-4.5")
|
||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
||||
.model(MODEL)
|
||||
.build();
|
||||
|
||||
// 调用
|
||||
@@ -77,7 +92,7 @@ public class ZhiPuAiChatModelTests {
|
||||
// 打印结果
|
||||
flux.doOnNext(response -> {
|
||||
// System.out.println(response);
|
||||
System.out.println(response.getResult().getOutput());
|
||||
System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
|
||||
}).then().block();
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
|
||||
|
||||
import cn.hutool.system.SystemUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.image.ImageOptions;
|
||||
@@ -9,6 +10,8 @@ import org.springframework.ai.openai.OpenAiImageModel;
|
||||
import org.springframework.ai.openai.OpenAiImageOptions;
|
||||
import org.springframework.ai.openai.api.OpenAiImageApi;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
|
||||
|
||||
/**
|
||||
* {@link OpenAiImageModel} 集成测试类
|
||||
*
|
||||
@@ -16,17 +19,20 @@ import org.springframework.ai.openai.api.OpenAiImageApi;
|
||||
*/
|
||||
public class OpenAiImageModelTests {
|
||||
|
||||
private static final String API_KEY = SystemUtil.get("OPENAI_API_KEY", "sk-xxxx");
|
||||
|
||||
private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder()
|
||||
.baseUrl("https://api.holdai.top") // apiKey
|
||||
.apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD")
|
||||
.apiKey(API_KEY)
|
||||
.build());
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
validateApiKey(API_KEY);
|
||||
// 准备参数
|
||||
ImageOptions options = OpenAiImageOptions.builder()
|
||||
.model(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜
|
||||
.model("gpt-image-1")
|
||||
.height(256).width(256)
|
||||
.build();
|
||||
ImagePrompt prompt = new ImagePrompt("中国长城!", options);
|
||||
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springaicommunity.qianfan.QianFanImageModel;
|
||||
import org.springaicommunity.qianfan.QianFanImageOptions;
|
||||
import org.springaicommunity.qianfan.api.QianFanImageApi;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.image.ImageResponse;
|
||||
|
||||
import static cn.iocoder.yudao.module.ai.framework.ai.core.model.image.StabilityAiImageModelTests.viewImage;
|
||||
|
||||
// TODO @芋艿:百度千帆 API 提供了 V2 版本,目前 Spring AI 不兼容,可关键 <https://github.com/spring-projects/spring-ai/issues/2179> 进展
|
||||
|
||||
/**
|
||||
* {@link QianFanImageModel} 集成测试类
|
||||
*/
|
||||
public class QianFanImageTests {
|
||||
|
||||
private final QianFanImageModel imageModel = new QianFanImageModel(
|
||||
new QianFanImageApi("qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e")); // 密钥
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
// 准备参数
|
||||
// 只支持 1024x1024、768x768、768x1024、1024x768、576x1024、1024x576
|
||||
QianFanImageOptions imageOptions = QianFanImageOptions.builder()
|
||||
.model(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue())
|
||||
.width(1024).height(1024)
|
||||
.N(1)
|
||||
.build();
|
||||
ImagePrompt prompt = new ImagePrompt("good", imageOptions);
|
||||
|
||||
// 方法调用
|
||||
ImageResponse response = imageModel.call(prompt);
|
||||
// 打印结果
|
||||
String b64Json = response.getResult().getOutput().getB64Json();
|
||||
System.out.println(response);
|
||||
viewImage(b64Json);
|
||||
}
|
||||
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.image.ImagePrompt;
|
||||
import org.springframework.ai.image.ImageResponse;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
|
||||
import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
|
||||
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
|
||||
|
||||
/**
|
||||
* {@link ZhiPuAiImageModel} 集成测试
|
||||
*/
|
||||
public class ZhiPuAiImageModelTests {
|
||||
|
||||
private final ZhiPuAiImageModel imageModel = new ZhiPuAiImageModel(
|
||||
new ZhiPuAiImageApi("78d3228c1d9e5e342a3e1ab349e2dd7b.VXLoq5vrwK2ofboy") // 密钥
|
||||
);
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testCall() {
|
||||
// 准备参数
|
||||
ZhiPuAiImageOptions imageOptions = ZhiPuAiImageOptions.builder()
|
||||
.model(ZhiPuAiImageApi.ImageModel.CogView_3.getValue())
|
||||
.build();
|
||||
ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions);
|
||||
|
||||
// 方法调用
|
||||
ImageResponse response = imageModel.call(prompt);
|
||||
// 打印结果
|
||||
System.out.println(response);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user