mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-17 22:03:39 +00:00
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出模型管理列表
|
* 导出模型管理列表
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务提供商名称
|
* 获取服务提供商名称
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user