fate:增加自定义模型,调整前端模型选择下拉框

This commit is contained in:
Administrator
2026-04-17 08:31:40 +08:00
parent c4f7c1f5d0
commit 2ee0aae57e
7 changed files with 143 additions and 1 deletions

View File

@@ -9,6 +9,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import org.ruoyi.common.chat.service.chat.IChatModelService; import org.ruoyi.common.chat.service.chat.IChatModelService;
import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo; import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import org.ruoyi.enums.ChatModeType;
import org.ruoyi.enums.ModelType; import org.ruoyi.enums.ModelType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated; 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.excel.utils.ExcelUtil;
import org.ruoyi.common.mybatis.core.page.TableDataInfo; 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)); return R.ok(chatModelService.queryList(bo));
} }
/**
* 获取模型供应商枚举
*/
@GetMapping("/providerOptions")
public R<List<LinkedHashMap<String, String>>> providerOptions() {
List<LinkedHashMap<String, String>> options = new java.util.ArrayList<>();
for (ChatModeType type : ChatModeType.values()) {
LinkedHashMap<String, String> item = new LinkedHashMap<>();
item.put("label", type.getDescription());
item.put("value", type.getCode());
options.add(item);
}
return R.ok(options);
}
/** /**
* 导出模型管理列表 * 导出模型管理列表
*/ */

View File

@@ -15,7 +15,8 @@ public enum ChatModeType {
DEEP_SEEK("deepseek", "深度求索"), DEEP_SEEK("deepseek", "深度求索"),
QIAN_WEN("qianwen", "通义千问"), QIAN_WEN("qianwen", "通义千问"),
OPEN_AI("openai", "openai"), OPEN_AI("openai", "openai"),
PPIO("ppio", "ppio"); PPIO("ppio", "ppio"),
CUSTOM_API("custom_api", "自定义API");
private final String code; private final String code;
private final String description; private final String description;

View File

@@ -1,9 +1,13 @@
package org.ruoyi.service.chat; package org.ruoyi.service.chat;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; 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.dto.request.ChatRequest;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo; import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import java.time.Duration;
/** /**
* 聊天消息Service接口 * 聊天消息Service接口
* *
@@ -21,6 +25,23 @@ public interface AbstractChatService {
*/ */
StreamingChatModel buildStreamingChatModel(ChatModelVo chatModelVo, ChatRequest chatRequest); 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();
}
/** /**
* 获取服务提供商名称 * 获取服务提供商名称
*/ */

View File

@@ -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;
}
}

View File

@@ -1,7 +1,9 @@
package org.ruoyi.service.chat.impl.provider; package org.ruoyi.service.chat.impl.provider;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaStreamingChatModel; import dev.langchain4j.model.ollama.OllamaStreamingChatModel;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -37,6 +39,14 @@ public class OllamaServiceImpl implements AbstractChatService {
.build(); .build();
} }
@Override
public ChatModel buildChatModel(ChatModelVo chatModelVo) {
return OllamaChatModel.builder()
.baseUrl(chatModelVo.getApiHost())
.modelName(chatModelVo.getModelName())
.build();
}
@Override @Override
public String getProviderName() { public String getProviderName() {
return ChatModeType.OLLAMA.getCode(); return ChatModeType.OLLAMA.getCode();

View File

@@ -1,7 +1,9 @@
package org.ruoyi.service.chat.impl.provider; package org.ruoyi.service.chat.impl.provider;
import dev.langchain4j.community.model.dashscope.QwenChatModel;
import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel; import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -38,6 +40,14 @@ public class QianWenChatServiceImpl implements AbstractChatService {
.build(); .build();
} }
@Override
public ChatModel buildChatModel(ChatModelVo chatModelVo) {
return QwenChatModel.builder()
.apiKey(chatModelVo.getApiKey())
.modelName(chatModelVo.getModelName())
.build();
}
@Override @Override
public String getProviderName() { public String getProviderName() {
return ChatModeType.QIAN_WEN.getCode(); return ChatModeType.QIAN_WEN.getCode();

View File

@@ -1,7 +1,9 @@
package org.ruoyi.service.chat.impl.provider; package org.ruoyi.service.chat.impl.provider;
import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel; import dev.langchain4j.community.model.zhipu.ZhipuAiStreamingChatModel;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel; import dev.langchain4j.model.chat.StreamingChatModel;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -35,6 +37,14 @@ public class ZhiPuChatServiceImpl implements AbstractChatService {
.build(); .build();
} }
@Override
public ChatModel buildChatModel(ChatModelVo chatModelVo) {
return ZhipuAiChatModel.builder()
.apiKey(chatModelVo.getApiKey())
.model(chatModelVo.getModelName())
.build();
}
@Override @Override
public String getProviderName() { public String getProviderName() {
return ChatModeType.ZHI_PU.getCode(); return ChatModeType.ZHI_PU.getCode();