From 2ee0aae57eb75d5b73bb00989da70acdb1b24cad Mon Sep 17 00:00:00 2001 From: Administrator <1037463791@qq.com> Date: Fri, 17 Apr 2026 08:31:40 +0800 Subject: [PATCH] =?UTF-8?q?fate=EF=BC=9A=E5=A2=9E=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E6=A8=A1=E5=9E=8B=EF=BC=8C=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=A8=A1=E5=9E=8B=E9=80=89=E6=8B=A9=E4=B8=8B?= =?UTF-8?q?=E6=8B=89=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/chat/ChatModelController.java | 18 +++++ .../java/org/ruoyi/enums/ChatModeType.java | 3 +- .../service/chat/AbstractChatService.java | 21 ++++++ .../impl/provider/CustomApiServiceImpl.java | 72 +++++++++++++++++++ .../chat/impl/provider/OllamaServiceImpl.java | 10 +++ .../impl/provider/QianWenChatServiceImpl.java | 10 +++ .../impl/provider/ZhiPuChatServiceImpl.java | 10 +++ 7 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/CustomApiServiceImpl.java diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/chat/ChatModelController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/chat/ChatModelController.java index 15884158..adf4322b 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/chat/ChatModelController.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/controller/chat/ChatModelController.java @@ -9,6 +9,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import org.ruoyi.common.chat.service.chat.IChatModelService; import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; +import org.ruoyi.enums.ChatModeType; import org.ruoyi.enums.ModelType; import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; @@ -23,6 +24,8 @@ import org.ruoyi.common.log.enums.BusinessType; import org.ruoyi.common.excel.utils.ExcelUtil; import org.ruoyi.common.mybatis.core.page.TableDataInfo; +import java.util.LinkedHashMap; + /** * 模型管理 * @@ -55,6 +58,21 @@ public class ChatModelController extends BaseController { return R.ok(chatModelService.queryList(bo)); } + /** + * 获取模型供应商枚举 + */ + @GetMapping("/providerOptions") + public R>> providerOptions() { + List> options = new java.util.ArrayList<>(); + for (ChatModeType type : ChatModeType.values()) { + LinkedHashMap item = new LinkedHashMap<>(); + item.put("label", type.getDescription()); + item.put("value", type.getCode()); + options.add(item); + } + return R.ok(options); + } + /** * 导出模型管理列表 */ diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java index c07d9444..1b91b8a2 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/enums/ChatModeType.java @@ -15,7 +15,8 @@ public enum ChatModeType { DEEP_SEEK("deepseek", "深度求索"), QIAN_WEN("qianwen", "通义千问"), OPEN_AI("openai", "openai"), - PPIO("ppio", "ppio"); + PPIO("ppio", "ppio"), + CUSTOM_API("custom_api", "自定义API"); private final String code; private final String description; diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/AbstractChatService.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/AbstractChatService.java index 8017fea2..91331c92 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/AbstractChatService.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/AbstractChatService.java @@ -1,9 +1,13 @@ package org.ruoyi.service.chat; +import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.chat.StreamingChatModel; +import dev.langchain4j.model.openai.OpenAiChatModel; import org.ruoyi.common.chat.domain.dto.request.ChatRequest; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; +import java.time.Duration; + /** * 聊天消息Service接口 * @@ -21,6 +25,23 @@ public interface AbstractChatService { */ StreamingChatModel buildStreamingChatModel(ChatModelVo chatModelVo, ChatRequest chatRequest); + /** + * 创建同步聊天模型(供 Agent/SupervisorAgent 使用) + * 默认实现使用 OpenAI 兼容协议,适用于 OpenAI、DeepSeek、PPIO 等兼容接口的 provider。 + * ZhiPu、QianWen、Ollama 等需覆盖此方法使用各自 SDK。 + * + * @param chatModelVo 模型配置 + * @return 同步聊天模型实例 + */ + default ChatModel buildChatModel(ChatModelVo chatModelVo) { + return OpenAiChatModel.builder() + .baseUrl(chatModelVo.getApiHost()) + .apiKey(chatModelVo.getApiKey()) + .modelName(chatModelVo.getModelName()) + .timeout(Duration.ofSeconds(120)) + .build(); + } + /** * 获取服务提供商名称 */ diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/CustomApiServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/CustomApiServiceImpl.java new file mode 100644 index 00000000..a4db18f7 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/CustomApiServiceImpl.java @@ -0,0 +1,72 @@ +package org.ruoyi.service.chat.impl.provider; + +import cn.hutool.core.util.StrUtil; +import dev.langchain4j.model.chat.ChatModel; +import dev.langchain4j.model.chat.StreamingChatModel; +import dev.langchain4j.model.openai.OpenAiChatModel; +import dev.langchain4j.model.openai.OpenAiStreamingChatModel; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.ruoyi.common.chat.domain.dto.request.ChatRequest; +import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; +import org.ruoyi.enums.ChatModeType; +import org.ruoyi.observability.MyChatModelListener; +import org.ruoyi.service.chat.AbstractChatService; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.List; + +/** + * 自定义 API 服务调用 + * + * 适用于 OpenAI 兼容接口或仅通过通用 HTTP 协议接入的第三方大模型服务。 + * 通过模型配置中的 apiHost / apiKey / modelName 即可复用,不需要再写死具体供应商。 + * + * @author better + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class CustomApiServiceImpl implements AbstractChatService { + + private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(180); + + @Override + public StreamingChatModel buildStreamingChatModel(ChatModelVo chatModelVo, ChatRequest chatRequest) { + return OpenAiStreamingChatModel.builder() + .baseUrl(normalizeBaseUrl(chatModelVo.getApiHost())) + .apiKey(defaultIfBlank(chatModelVo.getApiKey(), "EMPTY")) + .modelName(chatModelVo.getModelName()) + .timeout(DEFAULT_TIMEOUT) + .listeners(List.of(new MyChatModelListener())) + .returnThinking(chatRequest.getEnableThinking()) + .build(); + } + + @Override + public ChatModel buildChatModel(ChatModelVo chatModelVo) { + return OpenAiChatModel.builder() + .baseUrl(normalizeBaseUrl(chatModelVo.getApiHost())) + .apiKey(defaultIfBlank(chatModelVo.getApiKey(), "EMPTY")) + .modelName(chatModelVo.getModelName()) + .timeout(DEFAULT_TIMEOUT) + .build(); + } + + @Override + public String getProviderName() { + return ChatModeType.CUSTOM_API.getCode(); + } + + private String normalizeBaseUrl(String baseUrl) { + if (StrUtil.isBlank(baseUrl)) { + throw new IllegalArgumentException("自定义API的请求地址(apiHost)不能为空"); + } + return baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; + } + + private String defaultIfBlank(String value, String defaultValue) { + return StrUtil.isBlank(value) ? defaultValue : value; + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/OllamaServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/OllamaServiceImpl.java index a9346538..240ed215 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/OllamaServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/OllamaServiceImpl.java @@ -1,7 +1,9 @@ package org.ruoyi.service.chat.impl.provider; +import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.chat.StreamingChatModel; +import dev.langchain4j.model.ollama.OllamaChatModel; import dev.langchain4j.model.ollama.OllamaStreamingChatModel; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -37,6 +39,14 @@ public class OllamaServiceImpl implements AbstractChatService { .build(); } + @Override + public ChatModel buildChatModel(ChatModelVo chatModelVo) { + return OllamaChatModel.builder() + .baseUrl(chatModelVo.getApiHost()) + .modelName(chatModelVo.getModelName()) + .build(); + } + @Override public String getProviderName() { return ChatModeType.OLLAMA.getCode(); diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/QianWenChatServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/QianWenChatServiceImpl.java index 73289939..2490dd2e 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/QianWenChatServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/QianWenChatServiceImpl.java @@ -1,7 +1,9 @@ package org.ruoyi.service.chat.impl.provider; +import dev.langchain4j.community.model.dashscope.QwenChatModel; import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel; +import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.chat.StreamingChatModel; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -38,6 +40,14 @@ public class QianWenChatServiceImpl implements AbstractChatService { .build(); } + @Override + public ChatModel buildChatModel(ChatModelVo chatModelVo) { + return QwenChatModel.builder() + .apiKey(chatModelVo.getApiKey()) + .modelName(chatModelVo.getModelName()) + .build(); + } + @Override public String getProviderName() { return ChatModeType.QIAN_WEN.getCode(); diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/ZhiPuChatServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/ZhiPuChatServiceImpl.java index 9e1b504d..920dbaf1 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/ZhiPuChatServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/ZhiPuChatServiceImpl.java @@ -1,7 +1,9 @@ package org.ruoyi.service.chat.impl.provider; +import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel; import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel; +import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.chat.StreamingChatModel; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -35,6 +37,14 @@ public class ZhiPuChatServiceImpl implements AbstractChatService { .build(); } + @Override + public ChatModel buildChatModel(ChatModelVo chatModelVo) { + return ZhipuAiChatModel.builder() + .apiKey(chatModelVo.getApiKey()) + .model(chatModelVo.getModelName()) + .build(); + } + @Override public String getProviderName() { return ChatModeType.ZHI_PU.getCode();