feat: 支持coze,dify,派欧云等三方模型调用

This commit is contained in:
ageer
2025-05-11 17:25:02 +08:00
parent 9c2586ab43
commit 84b8d6f675
100 changed files with 350 additions and 169 deletions

View File

@@ -226,7 +226,7 @@ springdoc:
# 标题
title: '标题RuoYi-Vue-Plus多租户管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
description: ' 用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: ${ruoyi.version}'
# 作者信息

View File

@@ -6,7 +6,6 @@ import cn.hutool.core.date.DateUnit;
import lombok.extern.slf4j.Slf4j;
/**
* 描述:
*
* @author https:www.unfbx.com
* @date 2023-03-10

View File

@@ -1,7 +1,6 @@
package org.ruoyi.common.chat.constant;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-06

View File

@@ -8,7 +8,7 @@ import java.math.BigDecimal;
import java.util.List;
/**
* 描述:金额消耗信息
* 金额消耗信息
*
* @author https:www.unfbx.com
* @since 2023-04-08

View File

@@ -8,7 +8,7 @@ import java.io.Serializable;
import java.math.BigDecimal;
/**
* 描述:余额查询接口返回值
* 余额查询接口返回值
*
* @author https:www.unfbx.com
* @since 2023-03-18

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.util.List;
/**
* 描述:金额消耗列表
* 金额消耗列表
*
* @author https:www.unfbx.com
* @since 2023-04-08

View File

@@ -7,7 +7,6 @@ import lombok.Data;
import java.math.BigDecimal;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-18

View File

@@ -7,7 +7,6 @@ import lombok.Data;
import java.util.List;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-18

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.math.BigDecimal;
/**
* 描述:金额消耗列表
* 金额消耗列表
*
* @author https:www.unfbx.com
* @since 2023-04-08

View File

@@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-04-08

View File

@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 描述:账户信息
* 账户信息
*
* @author https:www.unfbx.com
* @since 2023-04-08

View File

@@ -13,7 +13,7 @@ import java.util.Map;
import static org.ruoyi.common.chat.entity.chat.BaseChatCompletion.Model.GPT_3_5_TURBO;
/**
* 描述: chat模型基础类
* chat模型基础类
*
* @author https:www.unfbx.com
* @since 1.1.2

View File

@@ -12,7 +12,6 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 1.1.2

View File

@@ -7,7 +7,6 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-02

View File

@@ -12,7 +12,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述: chat模型参数
* chat模型参数
*
* @author https:www.unfbx.com
* 2023-03-02

View File

@@ -8,7 +8,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述: chat答案类
* chat答案类
*
* @author https:www.unfbx.com
* 2023-03-02

View File

@@ -11,7 +11,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述 chat模型附带图片的参数
* chat模型附带图片的参数
*
* @author https:www.unfbx.com
* @since 1.1.2

View File

@@ -6,7 +6,6 @@ import lombok.*;
import lombok.extern.slf4j.Slf4j;
/**
* 描述:
*
* @author https://www.unfbx.com
* @since 1.1.2

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 描述:函数调用返回值
* 函数调用返回值
*
* @author https://www.unfbx.com
* @since 2023-06-14

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:方法参数实体类,实例数据如下
* 方法参数实体类,实例数据如下
* <pre>
* {
* "name": "get_current_weather",

View File

@@ -8,7 +8,6 @@ import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* 描述:
*
* @author https://www.unfbx.com
* 2023-11-10

View File

@@ -10,7 +10,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* @since 2023-03-02

View File

@@ -10,8 +10,6 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-02
*/

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 描述:方法参数类扩展参数可以继承Parameters自己实现
* 方法参数类扩展参数可以继承Parameters自己实现
* 参考:
* <pre>
* {

View File

@@ -7,7 +7,6 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -10,7 +10,7 @@ import java.util.List;
import java.util.Map;
/**
* 描述: 问题类
* 问题类
*
* @author https:www.unfbx.com
* 2023-02-11

View File

@@ -9,7 +9,7 @@ import org.ruoyi.common.chat.entity.common.Usage;
import java.io.Serializable;
/**
* 描述: 答案类
* 答案类
*
* @author https:www.unfbx.com
* 2023-02-11

View File

@@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -9,7 +9,7 @@ import org.ruoyi.common.chat.entity.common.Usage;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -9,7 +9,7 @@ import java.util.List;
import java.util.Objects;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -8,7 +8,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -11,7 +11,7 @@ import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -11,7 +11,7 @@ import java.io.Serializable;
import java.util.Objects;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -12,7 +12,7 @@ import java.io.Serializable;
import java.util.Objects;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -6,7 +6,7 @@ import lombok.Getter;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -6,7 +6,7 @@ import lombok.Getter;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -8,7 +8,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -8,7 +8,7 @@ import java.io.Serializable;
import java.math.BigDecimal;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -10,7 +10,7 @@ import java.util.List;
import java.util.Objects;
/**
* 描述:文本审核,敏感词鉴别
* 文本审核,敏感词鉴别
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -7,7 +7,7 @@ import lombok.Getter;
import java.io.Serializable;
/**
* 描述:语音转文字
* 语音转文字
*
* @author https:www.unfbx.com
* @since 2023-03-02

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* @since 2023-03-02

View File

@@ -16,7 +16,7 @@ import org.springframework.web.socket.WebSocketSession;
import java.util.Objects;
/**
* 描述:OpenAI流式输出Socket接收
* OpenAI流式输出Socket接收
*
* @author https:www.unfbx.com
* @date 2023-03-23

View File

@@ -40,7 +40,7 @@ import java.time.LocalDate;
import java.util.Map;
/**
* 描述: open ai官方api接口
* open ai官方api接口
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -56,7 +56,7 @@ import java.util.concurrent.TimeUnit;
/**
* 描述: open ai 客户端
* open ai 客户端
*
* @author https:www.unfbx.com
* @since 2023-02-11

View File

@@ -53,7 +53,7 @@ import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* 描述: open ai 客户端
* open ai 客户端
*
* @author https:www.unfbx.com
* 2023-02-28
@@ -190,7 +190,7 @@ public class OpenAiStreamClient {
ObjectMapper mapper = new ObjectMapper();
String requestBody = mapper.writeValueAsString(chatCompletion);
Request request = new Request.Builder()
.url(this.apiHost + apiUrl)
.url(this.apiHost)
.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))
.build();
factory.newEventSource(request, eventSourceListener);

View File

@@ -1,7 +1,7 @@
package org.ruoyi.common.chat.openai.exception;
/**
* 描述: 错误
* 错误
*
* @author https:www.unfbx.com
* 2023-02-11

View File

@@ -1,6 +1,6 @@
package org.ruoyi.common.chat.openai.exception;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-11

View File

@@ -5,7 +5,7 @@ import cn.hutool.core.util.RandomUtil;
import java.util.List;
/**
* 描述:随机策略
* 随机策略
*
* @author https:www.unfbx.com
* @since 2023-04-03

View File

@@ -3,7 +3,7 @@ package org.ruoyi.common.chat.openai.function;
import java.util.function.Function;
/**
* 描述:key 的获取策略
* key 的获取策略
* jdk默认实现
* @see Function
*

View File

@@ -9,7 +9,7 @@ import java.util.List;
import java.util.Map;
/**
* 描述:请求增加header apikey
* 请求增加header apikey
*
* @author https:www.unfbx.com
* @since 2023-03-23

View File

@@ -16,7 +16,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
/**
* 描述:动态处理key的鉴权拦截器
* 动态处理key的鉴权拦截器
*
* @author https:www.unfbx.com
* @since 2023-04-25

View File

@@ -4,7 +4,7 @@ import lombok.extern.slf4j.Slf4j;
import okhttp3.logging.HttpLoggingInterceptor;
/**
* 描述: 日志
* 日志
*
* @author https:www.unfbx.com
* 2023-02-28

View File

@@ -13,7 +13,7 @@ import java.io.IOException;
import java.util.Objects;
/**
* 描述:openai 返回值处理Interceptor
* openai 返回值处理Interceptor
*
* @author https:www.unfbx.com
* @since 2023-03-23

View File

@@ -7,7 +7,7 @@ import org.ruoyi.common.chat.entity.chat.Message;
import java.util.List;
/**
* 描述:对话请求对象
* 对话请求对象
*
* @author ageerle
* @sine 2023-04-08

View File

@@ -4,7 +4,7 @@ import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* @sine 2023-04-08

View File

@@ -10,7 +10,7 @@ import okhttp3.sse.EventSourceListener;
import java.util.Objects;
/**
* 描述: sse
* sse
*
* @author https:www.unfbx.com
* 2023-02-28

View File

@@ -8,7 +8,7 @@ import org.ruoyi.common.chat.openai.OpenAiStreamClient;
import org.ruoyi.common.chat.openai.plugin.PluginAbstract;
/**
* 描述: 插件开发返回信息收集sse监听器
* 插件开发返回信息收集sse监听器
*
* @author https:www.unfbx.com
* 2023-08-18

View File

@@ -19,7 +19,7 @@ import org.ruoyi.common.chat.openai.plugin.PluginParam;
import java.util.Objects;
/**
* 描述: 插件开发返回信息收集sse监听器
* 插件开发返回信息收集sse监听器
*
* @author https:www.unfbx.com
* 2023-08-18

View File

@@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* 描述:token计算工具类
* token计算工具类
*
* @author https:www.unfbx.com
* @since 2023-04-04

View File

@@ -3,7 +3,7 @@ package org.ruoyi.common.core.event;
import org.springframework.context.ApplicationEvent;
/**
* 描述:定义一个事件类,用于通知配置变化
* 定义一个事件类,用于通知配置变化
*
* @author ageerle@163.com
* date 2024/5/19

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* @since 2023-03-02

View File

@@ -7,7 +7,7 @@ import java.io.Serializable;
import java.util.List;
/**
* 描述: chat答案类
* chat答案类
*
* @author https:www.unfbx.com
* 2023-03-02

View File

@@ -9,7 +9,7 @@ import lombok.Getter;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* @since 2023-03-02

View File

@@ -7,7 +7,7 @@ import lombok.Data;
import java.io.Serializable;
/**
* 描述:
*
*
* @author https:www.unfbx.com
* 2023-02-15

View File

@@ -10,7 +10,7 @@ import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 描述:创建一个监听器,用于处理配置变化事件
* 创建一个监听器,用于处理配置变化事件
*
* @author ageerle@163.com
* date 2024/5/19

View File

@@ -71,6 +71,18 @@
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>io.github.imfangs</groupId>
<artifactId>dify-java-client</artifactId>
<version>1.0.7</version>
</dependency>
<dependency>
<groupId>com.coze</groupId>
<artifactId>coze-api</artifactId>
<version>0.3.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -47,7 +47,7 @@ public class ChatModel extends BaseEntity {
/**
* 模型价格
*/
private Long modelPrice;
private Double modelPrice;
/**
* 计费类型
@@ -69,11 +69,6 @@ public class ChatModel extends BaseEntity {
*/
private String apiHost;
/**
* 请求地址后缀 - 兼容多平台
*/
@ExcelProperty(value = "请求地址后缀")
private String apiUrl;
/**
* 密钥

View File

@@ -49,7 +49,7 @@ public class ChatModelBo extends BaseEntity {
* 模型价格
*/
@NotNull(message = "模型价格不能为空", groups = { AddGroup.class, EditGroup.class })
private Long modelPrice;
private Double modelPrice;
/**
* 计费类型
@@ -80,11 +80,6 @@ public class ChatModelBo extends BaseEntity {
@NotBlank(message = "密钥不能为空", groups = { AddGroup.class, EditGroup.class })
private String apiKey;
/**
* 请求地址后缀 - 兼容多平台
*/
@ExcelProperty(value = "请求地址后缀")
private String apiUrl;
/**
* 备注

View File

@@ -3,7 +3,7 @@ package org.ruoyi.domain.request;
import lombok.Data;
/**
* 描述:翻译请求对象
* 翻译请求对象
*
* @author ageerle@163.com
* date 2025/1/13

View File

@@ -56,7 +56,7 @@ public class ChatModelVo implements Serializable {
* 模型价格
*/
@ExcelProperty(value = "模型价格")
private Long modelPrice;
private Double modelPrice;
/**
* 计费类型
@@ -88,12 +88,6 @@ public class ChatModelVo implements Serializable {
@ExcelProperty(value = "密钥")
private String apiKey;
/**
* 请求地址后缀 - 兼容多平台
*/
@ExcelProperty(value = "请求地址后缀")
private String apiUrl;
/**
* 备注
*/

View File

@@ -7,7 +7,7 @@ import java.io.Serializable;
/**
* 描述:用户密码修改bo
* 用户密码修改bo
*
* @author ageerle@163.com
* date 2025/3/9

View File

@@ -32,12 +32,11 @@ public class ChatConfig {
public OpenAiStreamClient openAiStreamClient() {
String apiHost = configService.getConfigValue("chat", "apiHost");
String apiKey = configService.getConfigValue("chat", "apiKey");
String url = configService.getConfigValue("chat", "apiUrl");
openAiStreamClient = createOpenAiStreamClient(apiHost,apiKey,url);
openAiStreamClient = createOpenAiStreamClient(apiHost,apiKey);
return openAiStreamClient;
}
public static OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey,String url) {
public static OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
@@ -48,7 +47,6 @@ public class ChatConfig {
.build();
return OpenAiStreamClient.builder()
.apiHost(apiHost)
.apiUrl(url)
.apiKey(Collections.singletonList(apiKey))
.keyStrategy(new KeyRandomStrategy())
.okHttpClient(okHttpClient)

View File

@@ -28,7 +28,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
/**
* 描述:聊天管理
* 聊天管理
*
* @author ageerle@163.com
* @date 2023-03-01

View File

@@ -3,7 +3,7 @@ package org.ruoyi.chat.domain.bo;
import lombok.Data;
/**
* 描述:文生视频请求对象
* 文生视频请求对象
*
* @author ageerle@163.com
* date 2024/6/27

View File

@@ -3,7 +3,7 @@ package org.ruoyi.chat.domain.bo;
import lombok.Data;
/**
* 描述:生成歌词
* 生成歌词
*
* @author ageerle@163.com
* date 2024/6/27

View File

@@ -6,6 +6,8 @@ import lombok.Getter;
public enum ChatModeType {
OLLAMA("ollama", "本地部署模型"),
CHAT("chat", "中转模型"),
DIFY("dify", "DIFY"),
COZE("coze", "扣子"),
VECTOR("vector", "知识库向量模型");
private final String code;

View File

@@ -3,7 +3,7 @@ package org.ruoyi.chat.enums;
import lombok.Getter;
/**
* 描述:是否显示
* 是否显示
*
* @author ageerle@163.com
* date 2025/4/10

View File

@@ -0,0 +1,43 @@
package org.ruoyi.chat.factory;
import org.ruoyi.chat.service.chat.IChatService;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 聊天服务工厂类
*
* @author ageerle@163.com
* date 2025/5/10
*/
@Component
public class ChatServiceFactory implements ApplicationContextAware {
private final Map<String, IChatService> chatServiceMap = new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 初始化时收集所有IChatService的实现
Map<String, IChatService> serviceMap = applicationContext.getBeansOfType(IChatService.class);
for (IChatService service : serviceMap.values()) {
if (service != null) {
chatServiceMap.put(service.getCategory(), service);
}
}
}
/**
* 根据模型类别获取对应的聊天服务实现
*/
public IChatService getChatService(String category) {
IChatService service = chatServiceMap.get(category);
if (service == null) {
throw new IllegalArgumentException("不支持的模型类别: " + category);
}
return service;
}
}

View File

@@ -24,7 +24,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Objects;
/**
* 描述:OpenAIEventSourceListener
* OpenAIEventSourceListener
*
* @author https:www.unfbx.com
* @date 2023-02-22

View File

@@ -16,6 +16,8 @@ public interface IChatService {
* @param chatRequest 请求对象
*/
SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter);
/**
* 获取此服务支持的模型类别
*/
String getCategory();
}

View File

@@ -1,26 +1,16 @@
package org.ruoyi.chat.service.chat;
import cn.dev33.satoken.stp.StpUtil;
import lombok.RequiredArgsConstructor;
import org.ruoyi.chat.enums.DisplayType;
import org.ruoyi.chat.enums.UserGradeType;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.domain.bo.ChatModelBo;
import org.ruoyi.domain.bo.ChatPackagePlanBo;
import org.ruoyi.domain.vo.ChatModelVo;
import org.ruoyi.domain.vo.ChatPackagePlanVo;
import org.ruoyi.service.IChatModelService;
import org.ruoyi.service.IChatPackagePlanService;
import org.ruoyi.system.domain.vo.SysUserVo;
import org.ruoyi.system.service.ISysUserService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 描述:用户模型信息
* 用户模型信息
*
* @author ageerle@163.com
* date 2025/4/10
@@ -32,30 +22,9 @@ public class UserModelService {
private final IChatModelService chatModelService;
private final ISysUserService userService;
private final IChatPackagePlanService packagePlanService;
public List<ChatModelVo> modelList(ChatModelBo bo) {
bo.setModelShow(DisplayType.VISIBLE.getCode());
List<ChatModelVo> chatModelList = chatModelService.queryList(bo);
ChatPackagePlanBo sysPackagePlanBo = new ChatPackagePlanBo();
if (StpUtil.isLogin()) {
Long userId = LoginHelper.getLoginUser().getUserId();
SysUserVo sysUserVo = userService.selectUserById(userId);
if (UserGradeType.UNPAID.getCode().equals(sysUserVo.getUserGrade())){
sysPackagePlanBo.setName("Free");
ChatPackagePlanVo chatPackagePlanVo = packagePlanService.queryList(sysPackagePlanBo).get(0);
List<String> array = new ArrayList<>(Arrays.asList(chatPackagePlanVo.getPlanDetail().split(",")));
chatModelList.removeIf(model -> !array.contains(model.getModelName()));
}
}else {
sysPackagePlanBo.setName("Visitor");
ChatPackagePlanVo sysPackagePlanVo = packagePlanService.queryList(sysPackagePlanBo).get(0);
List<String> array = new ArrayList<>(Arrays.asList(sysPackagePlanVo.getPlanDetail().split(",")));
chatModelList.removeIf(model -> !array.contains(model.getModelName()));
}
return new ArrayList<>(chatModelList);
return chatModelService.queryList(bo);
}
}

View File

@@ -0,0 +1,81 @@
package org.ruoyi.chat.service.chat.impl;
import com.coze.openapi.client.chat.CreateChatReq;
import com.coze.openapi.client.chat.model.ChatEvent;
import com.coze.openapi.client.chat.model.ChatEventType;
import com.coze.openapi.client.connversations.message.model.Message;
import com.coze.openapi.service.auth.TokenAuth;
import com.coze.openapi.service.config.Consts;
import com.coze.openapi.service.service.CozeAPI;
import io.reactivex.Flowable;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.chat.enums.ChatModeType;
import org.ruoyi.chat.service.chat.IChatService;
import org.ruoyi.common.chat.request.ChatRequest;
import org.ruoyi.domain.vo.ChatModelVo;
import org.ruoyi.service.IChatModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Collections;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 扣子聊天管理
*
* @author ageer
*/
@Service
@Slf4j
public class CozeServiceImpl implements IChatService {
@Autowired
private IChatModelService chatModelService;
@Override
public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) {
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
TokenAuth authCli = new TokenAuth(chatModelVo.getApiKey());
CozeAPI coze =
new CozeAPI.Builder()
.baseURL(chatModelVo.getApiHost())
.auth(authCli)
.readTimeout(10000)
.build();
CreateChatReq req =
CreateChatReq.builder()
.botID(chatModelVo.getModelName())
.userID(chatRequest.getUserId().toString())
.messages(Collections.singletonList(Message.buildUserQuestionText("What can you do?")))
.build();
Flowable<ChatEvent> resp = coze.chat().stream(req);
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> {
resp.blockingForEach(
event -> {
if (ChatEventType.CONVERSATION_MESSAGE_DELTA.equals(event.getEvent())) {
emitter.send(event.getMessage().getContent());
log.info("coze: {}", event.getMessage().getContent());
}
if (ChatEventType.CONVERSATION_CHAT_COMPLETED.equals(event.getEvent())) {
emitter.complete();
log.info("Token usage: {}", event.getChat().getUsage().getTokenCount());
}
}
);
coze.shutdownExecutor();
});
return emitter;
}
@Override
public String getCategory() {
return ChatModeType.COZE.getCode();
}
}

View File

@@ -0,0 +1,96 @@
package org.ruoyi.chat.service.chat.impl;
import io.github.imfangs.dify.client.DifyClient;
import io.github.imfangs.dify.client.DifyClientFactory;
import io.github.imfangs.dify.client.callback.ChatStreamCallback;
import io.github.imfangs.dify.client.enums.ResponseMode;
import io.github.imfangs.dify.client.event.ErrorEvent;
import io.github.imfangs.dify.client.event.MessageEndEvent;
import io.github.imfangs.dify.client.event.MessageEvent;
import io.github.imfangs.dify.client.model.DifyConfig;
import io.github.imfangs.dify.client.model.chat.ChatMessage;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.chat.enums.ChatModeType;
import org.ruoyi.chat.service.chat.IChatService;
import org.ruoyi.common.chat.request.ChatRequest;
import org.ruoyi.domain.vo.ChatModelVo;
import org.ruoyi.service.IChatModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
/**
* dify 聊天管理
*
* @author ageer
*/
@Service
@Slf4j
public class DifyServiceImpl implements IChatService {
@Autowired
private IChatModelService chatModelService;
@Override
public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) {
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
// 使用自定义配置创建客户端
DifyConfig config = DifyConfig.builder()
.baseUrl(chatModelVo.getApiHost())
.apiKey(chatModelVo.getApiKey())
.connectTimeout(5000)
.readTimeout(60000)
.writeTimeout(30000)
.build();
DifyClient chatClient = DifyClientFactory.createClient(config);
// 创建聊天消息
ChatMessage message = ChatMessage.builder()
.query(chatRequest.getPrompt())
.user(chatRequest.getUserId().toString())
.responseMode(ResponseMode.STREAMING)
.build();
// 发送流式消息
try {
chatClient.sendChatMessageStream(message, new ChatStreamCallback() {
@SneakyThrows
@Override
public void onMessage(MessageEvent event) {
emitter.send(event.getAnswer());
log.info("收到消息片段: {}", event.getAnswer());
}
@Override
public void onMessageEnd(MessageEndEvent event) {
emitter.complete();
log.info("消息结束完整消息ID: {}", event.getMessageId());
}
@Override
public void onError(ErrorEvent event) {
System.err.println("错误: " + event.getMessage());
}
@Override
public void onException(Throwable throwable) {
System.err.println("异常: " + throwable.getMessage());
}
});
} catch (Exception e) {
log.error("dify请求失败{}", e.getMessage());
}
return emitter;
}
@Override
public String getCategory() {
return ChatModeType.DIFY.getCode();
}
}

View File

@@ -7,6 +7,8 @@ import io.github.ollama4j.models.chat.OllamaChatRequestBuilder;
import io.github.ollama4j.models.chat.OllamaChatRequestModel;
import io.github.ollama4j.models.generate.OllamaStreamHandler;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.chat.enums.ChatModeType;
import org.ruoyi.chat.service.chat.IChatService;
import org.ruoyi.chat.util.SSEUtil;
import org.ruoyi.common.chat.entity.chat.Message;
import org.ruoyi.common.chat.request.ChatRequest;
@@ -22,14 +24,18 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* @author ageer
*/
@Service
@Slf4j
public class OllamaServiceImpl {
public class OllamaServiceImpl implements IChatService {
@Autowired
private IChatModelService chatModelService;
@Autowired
private IChatModelService chatModelService;
public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
@Override
public SseEmitter chat(ChatRequest chatRequest, SseEmitter emitter) {
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
String host = chatModelVo.getApiHost();
List<Message> msgList = chatRequest.getMessages();
@@ -73,4 +79,8 @@ public class OllamaServiceImpl {
return emitter;
}
@Override
public String getCategory() {
return ChatModeType.OLLAMA.getCode();
}
}

View File

@@ -3,6 +3,7 @@ package org.ruoyi.chat.service.chat.impl;
import io.modelcontextprotocol.client.McpSyncClient;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.chat.config.ChatConfig;
import org.ruoyi.chat.enums.ChatModeType;
import org.ruoyi.chat.listener.SSEEventSourceListener;
import org.ruoyi.chat.service.chat.IChatService;
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
@@ -21,6 +22,9 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.List;
/**
* @author ageer
*/
@Service
@Slf4j
public class OpenAIServiceImpl implements IChatService {
@@ -28,9 +32,6 @@ public class OpenAIServiceImpl implements IChatService {
@Autowired
private IChatModelService chatModelService;
private OpenAiStreamClient openAiStreamClient;
@Value("${spring.ai.mcp.client.enabled}")
private Boolean enabled;
@@ -47,7 +48,7 @@ public class OpenAIServiceImpl implements IChatService {
@Override
public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
openAiStreamClient = ChatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey(),chatModelVo.getApiUrl());
OpenAiStreamClient openAiStreamClient = ChatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
List<Message> messages = chatRequest.getMessages();
if (enabled) {
String toolString = mcpChat(chatRequest.getPrompt());
@@ -69,4 +70,9 @@ public class OpenAIServiceImpl implements IChatService {
return this.chatClient.prompt(prompt).call().content();
}
@Override
public String getCategory() {
return ChatModeType.CHAT.getCode();
}
}

View File

@@ -8,7 +8,9 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ResponseBody;
import org.ruoyi.chat.enums.ChatModeType;
import org.ruoyi.chat.factory.ChatServiceFactory;
import org.ruoyi.chat.service.chat.IChatCostService;
import org.ruoyi.chat.service.chat.IChatService;
import org.ruoyi.chat.service.chat.ISseService;
import org.ruoyi.chat.util.IpUtil;
import org.ruoyi.chat.util.SSEUtil;
@@ -61,9 +63,7 @@ public class SseServiceImpl implements ISseService {
private final IChatModelService chatModelService;
private final OpenAIServiceImpl openAIService;
private final OllamaServiceImpl ollamaService;
private final ChatServiceFactory chatServiceFactory;
private final IChatSessionService chatSessionService;
@@ -95,7 +95,8 @@ public class SseServiceImpl implements ISseService {
chatCostService.deductToken(chatRequest);
}
// 根据模型分类调用不同的处理逻辑
switchModelAndHandle(chatRequest,sseEmitter);
IChatService chatService = chatServiceFactory.getChatService(chatModelVo.getCategory());
chatService.chat(chatRequest, sseEmitter);
} catch (Exception e) {
log.error(e.getMessage(),e);
SSEUtil.sendErrorEvent(sseEmitter,e.getMessage());
@@ -147,17 +148,6 @@ public class SseServiceImpl implements ISseService {
}
}
/**
* 根据模型名称前缀调用不同的处理逻辑
*/
private void switchModelAndHandle(ChatRequest chatRequest,SseEmitter emitter) {
// 调用ollama中部署的本地模型
if (ChatModeType.OLLAMA.getCode().equals(chatModelVo.getCategory())) {
ollamaService.chat(chatRequest,emitter);
} else {
openAIService.chat(chatRequest,emitter);
}
}
/**
* 构建消息列表

View File

@@ -26,7 +26,7 @@ import org.springframework.stereotype.Service;
import java.util.UUID;
/**
* 描述:微信公众号登录
* 微信公众号登录
*
* @author ageerle@163.com
* date 2025/4/30