mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-03 23:16:12 +00:00
context:工作流和Ai Chat对话消息功能整合
This commit is contained in:
@@ -81,12 +81,6 @@
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>${swagger-annotations.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.api-client</groupId>
|
||||
<artifactId>google-api-client</artifactId>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.ruoyi.workflow.base;
|
||||
|
||||
import lombok.Data;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
package org.ruoyi.workflow.base;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.common.core.domain.model.LoginUser;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.workflow.entity.User;
|
||||
import org.ruoyi.workflow.enums.UserStatusEnum;
|
||||
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.A_USER_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* 线程上下文适配器,统一接入 Sa-Token 登录态。
|
||||
*/
|
||||
public class ThreadContext {
|
||||
|
||||
private static final ThreadLocal<User> CURRENT_USER = new ThreadLocal<>();
|
||||
private static final ThreadLocal<String> CURRENT_TOKEN = new ThreadLocal<>();
|
||||
|
||||
private ThreadContext() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录的工作流用户。
|
||||
*/
|
||||
public static User getCurrentUser() {
|
||||
User cached = CURRENT_USER.get();
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
throw new BaseException(A_USER_NOT_FOUND.getInfo());
|
||||
}
|
||||
User mapped = mapToWorkflowUser(loginUser);
|
||||
CURRENT_USER.set(mapped);
|
||||
return mapped;
|
||||
}
|
||||
|
||||
/**
|
||||
* 允许在测试或特殊场景下显式设置当前用户。
|
||||
*/
|
||||
public static void setCurrentUser(User user) {
|
||||
if (user == null) {
|
||||
CURRENT_USER.remove();
|
||||
} else {
|
||||
CURRENT_USER.set(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户 ID。
|
||||
*/
|
||||
public static Long getCurrentUserId() {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
if (userId != null) {
|
||||
return userId;
|
||||
}
|
||||
return getCurrentUser().getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前访问 token。
|
||||
*/
|
||||
public static String getToken() {
|
||||
String token = CURRENT_TOKEN.get();
|
||||
if (StringUtils.isNotBlank(token)) {
|
||||
return token;
|
||||
}
|
||||
try {
|
||||
token = StpUtil.getTokenValue();
|
||||
} catch (Exception ignore) {
|
||||
token = null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(token)) {
|
||||
CURRENT_TOKEN.set(token);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
public static void setToken(String token) {
|
||||
if (StringUtils.isBlank(token)) {
|
||||
CURRENT_TOKEN.remove();
|
||||
} else {
|
||||
CURRENT_TOKEN.set(token);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLogin() {
|
||||
return LoginHelper.isLogin();
|
||||
}
|
||||
|
||||
public static User getExistCurrentUser() {
|
||||
return getCurrentUser();
|
||||
}
|
||||
|
||||
public static void unload() {
|
||||
CURRENT_USER.remove();
|
||||
CURRENT_TOKEN.remove();
|
||||
}
|
||||
|
||||
private static User mapToWorkflowUser(LoginUser loginUser) {
|
||||
User user = new User();
|
||||
user.setId(loginUser.getUserId());
|
||||
user.setName(loginUser.getUsername());
|
||||
user.setEmail(loginUser.getUsername());
|
||||
user.setUuid(String.valueOf(loginUser.getUserId()));
|
||||
user.setUserStatus(UserStatusEnum.NORMAL);
|
||||
user.setIsAdmin(LoginHelper.isSuperAdmin(loginUser.getUserId()));
|
||||
user.setUnderstandContextMsgPairNum(0);
|
||||
user.setQuotaByTokenDaily(0);
|
||||
user.setQuotaByTokenMonthly(0);
|
||||
user.setQuotaByRequestDaily(0);
|
||||
user.setQuotaByRequestMonthly(0);
|
||||
user.setQuotaByImageDaily(0);
|
||||
user.setQuotaByImageMonthly(0);
|
||||
user.setIsDeleted(false);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.ruoyi.common.chat.base.ThreadContext;
|
||||
import org.ruoyi.common.core.domain.R;
|
||||
import org.ruoyi.workflow.base.ThreadContext;
|
||||
import org.ruoyi.workflow.dto.workflow.*;
|
||||
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||
import org.ruoyi.workflow.service.WorkflowComponentService;
|
||||
@@ -72,7 +72,7 @@ public class WorkflowController {
|
||||
@Operation(summary = "流式响应")
|
||||
@PostMapping(value = "/run", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public SseEmitter sseAsk(@RequestBody WorkflowRunReq runReq) {
|
||||
return workflowStarter.streaming(ThreadContext.getCurrentUser(), runReq.getUuid(), runReq.getInputs());
|
||||
return workflowStarter.streaming(ThreadContext.getCurrentUser(), runReq.getUuid(), runReq.getInputs(),runReq.getSessionId());
|
||||
}
|
||||
|
||||
@GetMapping("/mine/search")
|
||||
|
||||
@@ -30,7 +30,7 @@ public class WorkflowRuntimeController {
|
||||
@Operation(summary = "接收用户输入以继续执行剩余流程")
|
||||
@PostMapping(value = "/resume/{runtimeUuid}")
|
||||
public R resume(@PathVariable String runtimeUuid, @RequestBody WorkflowResumeReq resumeReq) {
|
||||
workflowStarter.resumeFlow(runtimeUuid, resumeReq.getFeedbackContent());
|
||||
workflowStarter.resumeFlow(runtimeUuid, resumeReq.getFeedbackContent(), resumeReq.getSseEmitter());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package org.ruoyi.workflow.dto.workflow;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
@Data
|
||||
public class WorkflowResumeReq {
|
||||
private String feedbackContent;
|
||||
private SseEmitter sseEmitter;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
public class WorkflowRunReq {
|
||||
private List<ObjectNode> inputs;
|
||||
private String uuid;
|
||||
private Long sessionId;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.ruoyi.workflow.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class BaseEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField(value = "create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(value = "update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@Schema(title = "是否删除(0:未删除,1:已删除)")
|
||||
@TableField(value = "is_deleted")
|
||||
private Boolean isDeleted;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.ruoyi.workflow.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.workflow.enums.UserStatusEnum;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@TableName("adi_user")
|
||||
@Schema(title = "User对象")
|
||||
public class User extends BaseEntity {
|
||||
|
||||
@Schema(name = "用户名称")
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
@TableField("email")
|
||||
private String email;
|
||||
|
||||
@TableField("password")
|
||||
private String password;
|
||||
|
||||
@TableField("uuid")
|
||||
private String uuid;
|
||||
|
||||
@Schema(name = "上下文理解中需要携带的消息对数量(提示词及回复)")
|
||||
@TableField("understand_context_msg_pair_num")
|
||||
private Integer understandContextMsgPairNum;
|
||||
|
||||
@Schema(name = "token quota in one day")
|
||||
@TableField("quota_by_token_daily")
|
||||
private Integer quotaByTokenDaily;
|
||||
|
||||
@Schema(name = "token quota in one month")
|
||||
@TableField("quota_by_token_monthly")
|
||||
private Integer quotaByTokenMonthly;
|
||||
|
||||
@Schema(name = "request quota in one day")
|
||||
@TableField("quota_by_request_daily")
|
||||
private Integer quotaByRequestDaily;
|
||||
|
||||
@Schema(name = "request quota in one month")
|
||||
@TableField("quota_by_request_monthly")
|
||||
private Integer quotaByRequestMonthly;
|
||||
|
||||
@TableField("quota_by_image_daily")
|
||||
private Integer quotaByImageDaily;
|
||||
|
||||
@TableField("quota_by_image_monthly")
|
||||
private Integer quotaByImageMonthly;
|
||||
|
||||
@TableField("user_status")
|
||||
private UserStatusEnum userStatus;
|
||||
|
||||
@TableField("active_time")
|
||||
private LocalDateTime activeTime;
|
||||
|
||||
@Schema(title = "是否管理员(0:否,1:是)")
|
||||
@TableField(value = "is_admin")
|
||||
private Boolean isAdmin;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.chat.entity.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.chat.entity.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.chat.entity.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.chat.entity.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.chat.entity.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.ruoyi.common.chat.entity.BaseEntity;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.ruoyi.workflow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.ruoyi.common.chat.enums.BaseEnum;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.ruoyi.workflow.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IEnum;
|
||||
|
||||
public interface BaseEnum extends IEnum<Integer> {
|
||||
/**
|
||||
* 获取对应名称
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String getDesc();
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package org.ruoyi.workflow.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum ErrorEnum {
|
||||
SUCCESS("00000", "成功"),
|
||||
A_URL_NOT_FOUND("A0001", "地址不存在"),
|
||||
A_PARAMS_ERROR("A0002", "参数校验不通过"),
|
||||
A_REQUEST_TOO_MUCH("A0003", "访问次数太多"),
|
||||
A_LOGIN_ERROR("A0004", "登陆失败,账号或密码错误"),
|
||||
A_LOGIN_ERROR_MAX("A0005", "失败次数太多,请输入验证码重试"),
|
||||
A_LOGIN_CAPTCHA_ERROR("A0006", "验证码不正确"),
|
||||
A_USER_NOT_EXIST("A0007", "用户不存在"),
|
||||
A_CONVERSATION_NOT_EXIST("A0008", "对话不存在"),
|
||||
A_IMAGE_NUMBER_ERROR("A0009", "图片数量不对"),
|
||||
A_IMAGE_SIZE_ERROR("A0010", "图片尺寸不对"),
|
||||
A_FILE_NOT_EXIST("A0011", "文件不存在"),
|
||||
A_DRAWING("A0012", "作图还未完成"),
|
||||
A_USER_EXIST("A0013", "账号已经存在,请使用账号密码登录"),
|
||||
A_FIND_PASSWORD_CODE_ERROR("A0014", "重置码已过期或不存在"),
|
||||
A_USER_WAIT_CONFIRM("A0015", "用户未激活"),
|
||||
A_USER_NOT_AUTH("A0016", "用户无权限"),
|
||||
A_DATA_NOT_FOUND("A0017", "数据不存在"),
|
||||
A_UPLOAD_FAIL("A0018", "上传失败"),
|
||||
A_QA_ASK_LIMIT("A0019", "请求次数太多"),
|
||||
A_QA_ITEM_LIMIT("A0020", "知识点生成已超额度"),
|
||||
A_CONVERSATION_EXIST("A0021", "会话(角色)已存在"),
|
||||
A_MODEL_NOT_FOUND("A0022", "模型不存在"),
|
||||
A_MODEL_ALREADY_EXIST("A0023", "模型已存在"),
|
||||
A_CONVERSATION_NOT_FOUND("A0024", "会话(角色)找不到"),
|
||||
A_AI_IMAGE_NOT_FOUND("A0024", "图片找不到"),
|
||||
A_ENABLE_MODEL_NOT_FOUND("A0025", "没有可用的模型"),
|
||||
A_DOC_INDEX_DOING("A0026", "文档索引正在进行中,请稍后重试"),
|
||||
A_PRESET_CONVERSATION_NOT_EXIST("A0027", "预设会话或角色不存在"),
|
||||
A_CONVERSATION_TITLE_EXIST("A0028", "会话(角色)标题已存在"),
|
||||
A_AI_IMAGE_NO_AUTH("A0029", "无权限查看该图片"),
|
||||
A_USER_NOT_FOUND("A0030", "用户不存在"),
|
||||
A_ACTIVE_CODE_INVALID("A0031", "激活码已失效"),
|
||||
A_OLD_PASSWORD_INVALID("A0032", "原密码不正确"),
|
||||
A_OPT_TOO_FREQUENTLY("A0032", "操作太频繁"),
|
||||
A_DRAW_NOT_FOUND("A00033", "绘图记录找不到"),
|
||||
A_WF_NOT_FOUND("A00034", "工作流找不到"),
|
||||
A_WF_DISABLED("A0035", "工作流已停用"),
|
||||
A_WF_NODE_NOT_FOUND("A0036", "工作流节点找不到"),
|
||||
A_WF_NODE_CONFIG_NOT_FOUND("A0037", "工作流节点配置找不到"),
|
||||
A_WF_NODE_CONFIG_ERROR("A0038", "工作流节点配置异常"),
|
||||
A_WF_INPUT_INVALID("A0039", "工作流输入参数错误"),
|
||||
A_WF_INPUT_MISSING("A0040", "工作流输入缺少参数"),
|
||||
A_WF_MULTIPLE_START_NODE("A0041", "多个开始节点"),
|
||||
A_WF_START_NODE_NOT_FOUND("A0042", "没有开始节点"),
|
||||
A_WF_END_NODE_NOT_FOUND("A0043", "没有结束节点"),
|
||||
A_WF_EDGE_NOT_FOUND("A0044", "工作流的边找不到"),
|
||||
A_WF_RUNTIME_NOT_FOUND("A00045", "工作流运行时数据找不到"),
|
||||
A_SEARCH_QUERY_IS_EMPTY("A00046", "搜索内容不能为空"),
|
||||
A_WF_COMPONENT_NOT_FOUND("A00047", "工作流基础组件找不到"),
|
||||
A_WF_RESUME_FAIL("A00048", "工作流恢复执行时失败"),
|
||||
A_MAIL_SENDER_EMPTY("A00049", "邮件发送人不能为空"),
|
||||
A_MAIL_SENDER_CONFIG_ERROR("A00050", "邮件发送人配置错误"),
|
||||
A_MAIL_RECEIVER_EMPTY("A00051", "邮件接收人不能为空"),
|
||||
A_MCP_SERVER_NOT_FOUND("A00052", "MCP服务找不到"),
|
||||
A_USER_MCP_SERVER_NOT_FOUND("A00053", "用户的MCP服务找不到"),
|
||||
A_PARAMS_INVALID_BY_("A00054", "参数校验异常:{0}"),
|
||||
A_AI_MESSAGE_NOT_FOUND("A00055", "找不到AI的消息"),
|
||||
A_USER_QUESTION_NOT_FOUND("A00056", "用户问题不存在"),
|
||||
A_PLATFORM_NOT_MATCH("A0057", "平台不匹配"),
|
||||
B_UNCAUGHT_ERROR("B0001", "未捕捉异常"),
|
||||
B_COMMON_ERROR("B0002", "业务出错"),
|
||||
B_GLOBAL_ERROR("B0003", "全局异常"),
|
||||
B_SAVE_IMAGE_ERROR("B0004", "保存图片异常"),
|
||||
B_FIND_IMAGE_404("B0005", "无法找到图片"),
|
||||
B_DAILY_QUOTA_USED("B0006", "今天额度已经用完"),
|
||||
B_MONTHLY_QUOTA_USED("B0007", "当月额度已经用完"),
|
||||
B_LLM_NOT_SUPPORT("B0008", "LLM不支持该功能"),
|
||||
B_LLM_SECRET_KEY_NOT_SET("B0009", "LLM的secret key没设置"),
|
||||
B_MESSAGE_NOT_FOUND("B0008", "消息不存在"),
|
||||
B_LLM_SERVICE_DISABLED("B0009", "LLM服务不可用"),
|
||||
B_KNOWLEDGE_BASE_IS_EMPTY("B0010", "知识库内容为空"),
|
||||
B_NO_ANSWER("B0011", "[无答案]"),
|
||||
B_SAVE_FILE_ERROR("B0012", "保存文件异常"),
|
||||
B_BREAK_SEARCH("B0013", "中断搜索"),
|
||||
B_GRAPH_FILTER_NOT_FOUND("B0014", "图过滤器未定义"),
|
||||
B_DB_ERROR("B0015", "数据库查询异常"),
|
||||
B_ACTIVE_USER_ERROR("B0016", "激活用户失败"),
|
||||
B_RESET_PASSWORD_ERROR("B0017", "重置密码失败"),
|
||||
B_IMAGE_LOAD_ERROR("B0018", "加载图片失败"),
|
||||
B_IO_EXCEPTION("B0019", "IO异常"),
|
||||
B_SERVER_EXCEPTION("B0020", "服务端异常"),
|
||||
B_DELETE_FILE_ERROR("B0021", "删除文件异常"),
|
||||
B_WF_RUN_ERROR("B0022", "工作流运行异常"),
|
||||
B_WF_NODE_DEFINITION_NOT_FOUND("B0023", "工作流节点定义找不到"),
|
||||
B_DIR_CREATE_FAIL("B0024", "创建目录失败"),
|
||||
B_LLM_TEMPERATURE_ERROR("B0025", "采样温度应该在 0.1-1之间"),
|
||||
B_ASR_SETTING_NOT_FOUND("B0026", "语音识别设置未找到"),
|
||||
B_URL_INVALID("B0027", "不是有效的网络地址"),
|
||||
B_ASR_MODEL_NOT_FOUND("B0028", "语音识别模型未找到"),
|
||||
B_TTS_SETTING_NOT_FOUND("B0029", "语音合成设置未找到"),
|
||||
B_TTS_MODEL_NOT_FOUND("B0030", "语音合成模型未找到"),
|
||||
B_VOICE_NOT_FOUND("B0031", "声音不存在"),
|
||||
C_DRAW_FAIL("C0001", "大模型生成图片失败,原因:{0}"),
|
||||
C_ALI_OSS_CONFIG_ERROR("C0002", "阿里云OSS初始化失败,原因:{0}"),
|
||||
C_LLM_RESPONSE_INVALID("C0003", "大模型生成结果内容无效"),
|
||||
C_WF_COMPONENT_DELETED_FAIL_BY_USED("C0004", "工作流组件已经被使用,无法被删除,可先停用");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
ErrorEnum(String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public static ErrorEnum getErrorEnum(String code) {
|
||||
ErrorEnum result = null;
|
||||
for (ErrorEnum c : ErrorEnum.values()) {
|
||||
if (c.getCode().equals(code)) {
|
||||
result = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null == result) {
|
||||
result = B_COMMON_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.ruoyi.workflow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UserStatusEnum implements BaseEnum {
|
||||
|
||||
WAIT_CONFIRM(1, "待验证"),
|
||||
NORMAL(2, "正常"),
|
||||
FREEZE(3, "冻结");
|
||||
|
||||
private final Integer value;
|
||||
private final String desc;
|
||||
|
||||
public static UserStatusEnum getByValue(Integer val) {
|
||||
return Arrays.stream(UserStatusEnum.values()).filter(item -> item.value.equals(val)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.ruoyi.workflow.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.ruoyi.common.chat.enums.BaseEnum;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import com.google.common.cache.CacheBuilder;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.common.chat.entity.User;
|
||||
import org.ruoyi.workflow.cosntant.AdiConstant;
|
||||
import org.ruoyi.workflow.cosntant.RedisKeyConstant;
|
||||
import org.ruoyi.workflow.entity.User;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
@@ -7,11 +7,11 @@ import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.dto.workflow.WfComponentReq;
|
||||
import org.ruoyi.workflow.dto.workflow.WfComponentSearchReq;
|
||||
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.mapper.WorkflowComponentMapper;
|
||||
import org.ruoyi.workflow.util.PrivilegeUtil;
|
||||
import org.ruoyi.workflow.util.UuidUtil;
|
||||
@@ -26,7 +26,7 @@ import java.util.List;
|
||||
|
||||
import static org.ruoyi.workflow.cosntant.RedisKeyConstant.WORKFLOW_COMPONENTS;
|
||||
import static org.ruoyi.workflow.cosntant.RedisKeyConstant.WORKFLOW_COMPONENT_START_KEY;
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.C_WF_COMPONENT_DELETED_FAIL_BY_USED;
|
||||
import static org.ruoyi.common.chat.enums.ErrorEnum.C_WF_COMPONENT_DELETED_FAIL_BY_USED;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
|
||||
@@ -5,10 +5,10 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.dto.workflow.WfEdgeReq;
|
||||
import org.ruoyi.workflow.entity.WorkflowEdge;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.mapper.WorkflowEdgeMapper;
|
||||
import org.ruoyi.workflow.util.MPPageUtil;
|
||||
import org.ruoyi.workflow.util.UuidUtil;
|
||||
|
||||
@@ -6,12 +6,12 @@ import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.dto.workflow.WfNodeDto;
|
||||
import org.ruoyi.workflow.entity.Workflow;
|
||||
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||
import org.ruoyi.workflow.entity.WorkflowNode;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.enums.WfIODataTypeEnum;
|
||||
import org.ruoyi.workflow.mapper.WorkflowNodeMapper;
|
||||
import org.ruoyi.workflow.util.JsonUtil;
|
||||
|
||||
@@ -4,9 +4,9 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.workflow.base.ThreadContext;
|
||||
import org.ruoyi.common.chat.base.ThreadContext;
|
||||
import org.ruoyi.common.chat.entity.User;
|
||||
import org.ruoyi.workflow.dto.workflow.WfRuntimeNodeDto;
|
||||
import org.ruoyi.workflow.entity.User;
|
||||
import org.ruoyi.workflow.entity.WorkflowRuntimeNode;
|
||||
import org.ruoyi.workflow.mapper.WorkflowRuntimeNodeMapper;
|
||||
import org.ruoyi.workflow.util.JsonUtil;
|
||||
|
||||
@@ -7,13 +7,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.workflow.base.ThreadContext;
|
||||
import org.ruoyi.common.chat.base.ThreadContext;
|
||||
import org.ruoyi.common.chat.entity.User;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.dto.workflow.WfRuntimeNodeDto;
|
||||
import org.ruoyi.workflow.dto.workflow.WfRuntimeResp;
|
||||
import org.ruoyi.workflow.entity.User;
|
||||
import org.ruoyi.workflow.entity.Workflow;
|
||||
import org.ruoyi.workflow.entity.WorkflowRuntime;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.mapper.WorkflowRunMapper;
|
||||
import org.ruoyi.workflow.util.JsonUtil;
|
||||
import org.ruoyi.workflow.util.MPPageUtil;
|
||||
|
||||
@@ -6,15 +6,15 @@ import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.common.chat.base.ThreadContext;
|
||||
import org.ruoyi.common.chat.entity.User;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.base.ThreadContext;
|
||||
import org.ruoyi.workflow.dto.workflow.WfEdgeReq;
|
||||
import org.ruoyi.workflow.dto.workflow.WfNodeDto;
|
||||
import org.ruoyi.workflow.dto.workflow.WorkflowResp;
|
||||
import org.ruoyi.workflow.dto.workflow.WorkflowUpdateReq;
|
||||
import org.ruoyi.workflow.entity.User;
|
||||
import org.ruoyi.workflow.entity.Workflow;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.mapper.WorkflowMapper;
|
||||
import org.ruoyi.workflow.util.MPPageUtil;
|
||||
import org.ruoyi.workflow.util.PrivilegeUtil;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.ruoyi.workflow.util;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
|
||||
import org.ruoyi.common.chat.base.ThreadContext;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.base.ThreadContext;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
|
||||
import static org.ruoyi.workflow.cosntant.AdiConstant.*;
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.ruoyi.workflow.util;
|
||||
|
||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.enums.RoleType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.core.utils.SpringUtils;
|
||||
import org.ruoyi.workflow.workflow.WfState;
|
||||
import org.ruoyi.workflow.workflow.WorkflowUtil;
|
||||
|
||||
/**
|
||||
* 工作流消息工具类
|
||||
*
|
||||
* @author Zengxb
|
||||
* @date 2026-02-26
|
||||
*/
|
||||
@Slf4j
|
||||
public class WorkflowMessageUtil {
|
||||
|
||||
/**
|
||||
* 保存工作流消息公共方法(对话使用)
|
||||
* @param wfState 工作流实例状态
|
||||
* @param message 消息
|
||||
*/
|
||||
public static void saveWorkflowMessage(WfState wfState, String message) {
|
||||
Long sessionId = wfState.getSessionId();
|
||||
Long userId = wfState.getUserId();
|
||||
|
||||
if (sessionId != null && userId != null) {
|
||||
ChatRequest chatRequest = new ChatRequest();
|
||||
chatRequest.setSessionId(sessionId);
|
||||
WorkflowUtil workflowUtil = SpringUtils.getBean(WorkflowUtil.class);
|
||||
workflowUtil.saveChatMessage(chatRequest, userId, message, RoleType.WORKFLOW.getName(), new ChatModelVo());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.enums.WfIODataTypeEnum;
|
||||
import org.ruoyi.workflow.util.JsonUtil;
|
||||
import org.ruoyi.workflow.workflow.data.NodeIOData;
|
||||
|
||||
@@ -4,8 +4,8 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bsc.langgraph4j.langchain4j.generators.StreamingChatGenerator;
|
||||
import org.bsc.langgraph4j.state.AgentState;
|
||||
import org.ruoyi.common.chat.entity.User;
|
||||
import org.ruoyi.workflow.dto.workflow.WfRuntimeNodeDto;
|
||||
import org.ruoyi.workflow.entity.User;
|
||||
import org.ruoyi.workflow.entity.WorkflowNode;
|
||||
import org.ruoyi.workflow.workflow.data.NodeIOData;
|
||||
import org.ruoyi.workflow.workflow.node.AbstractWfNode;
|
||||
@@ -28,6 +28,7 @@ public class WfState {
|
||||
private Long userId;
|
||||
private String tokenValue;
|
||||
private SseEmitter sseEmitter;
|
||||
private Long sessionId;
|
||||
|
||||
//Source node uuid => target node uuid list
|
||||
private Map<String, List<String>> edges = new HashMap<>();
|
||||
@@ -59,13 +60,14 @@ public class WfState {
|
||||
*/
|
||||
private Set<String> interruptNodes = new HashSet<>();
|
||||
|
||||
public WfState(User user, List<NodeIOData> input, String uuid, Long userId, String tokenValue, SseEmitter sseEmitter) {
|
||||
public WfState(User user, List<NodeIOData> input, String uuid, Long userId, String tokenValue, SseEmitter sseEmitter, Long sessionId) {
|
||||
this.input = input;
|
||||
this.user = user;
|
||||
this.uuid = uuid;
|
||||
this.userId = userId;
|
||||
this.tokenValue = tokenValue;
|
||||
this.sseEmitter = sseEmitter;
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.ruoyi.workflow.workflow;
|
||||
import cn.hutool.core.collection.CollStreamUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -13,16 +15,18 @@ import org.bsc.langgraph4j.langchain4j.generators.StreamingChatGenerator;
|
||||
import org.bsc.langgraph4j.state.AgentState;
|
||||
import org.bsc.langgraph4j.state.StateSnapshot;
|
||||
import org.bsc.langgraph4j.streaming.StreamingOutput;
|
||||
import org.ruoyi.common.chat.entity.User;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
||||
import org.ruoyi.workflow.dto.workflow.WfRuntimeNodeDto;
|
||||
import org.ruoyi.workflow.dto.workflow.WfRuntimeResp;
|
||||
import org.ruoyi.workflow.entity.*;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
import org.ruoyi.workflow.helper.SSEEmitterHelper;
|
||||
import org.ruoyi.workflow.service.WorkflowRuntimeNodeService;
|
||||
import org.ruoyi.workflow.service.WorkflowRuntimeService;
|
||||
import org.ruoyi.workflow.util.JsonUtil;
|
||||
import org.ruoyi.workflow.util.WorkflowMessageUtil;
|
||||
import org.ruoyi.workflow.workflow.data.NodeIOData;
|
||||
import org.ruoyi.workflow.workflow.def.WfNodeIO;
|
||||
import org.ruoyi.workflow.workflow.def.WfNodeParamRef;
|
||||
@@ -34,7 +38,7 @@ import java.util.function.Function;
|
||||
|
||||
import static org.bsc.langgraph4j.StateGraph.END;
|
||||
import static org.ruoyi.workflow.cosntant.AdiConstant.WorkflowConstant.*;
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.*;
|
||||
import static org.ruoyi.common.chat.enums.ErrorEnum.*;
|
||||
|
||||
@Slf4j
|
||||
public class WorkflowEngine {
|
||||
@@ -45,7 +49,9 @@ public class WorkflowEngine {
|
||||
private final SSEEmitterHelper sseEmitterHelper;
|
||||
private final WorkflowRuntimeService workflowRuntimeService;
|
||||
private final WorkflowRuntimeNodeService workflowRuntimeNodeService;
|
||||
@Getter
|
||||
private CompiledGraph<WfNodeState> app;
|
||||
@Setter
|
||||
private SseEmitter sseEmitter;
|
||||
private User user;
|
||||
private WfState wfState;
|
||||
@@ -68,7 +74,7 @@ public class WorkflowEngine {
|
||||
this.workflowRuntimeNodeService = workflowRuntimeNodeService;
|
||||
}
|
||||
|
||||
public void run(User user, List<ObjectNode> userInputs, SseEmitter sseEmitter, Long userId, String tokenValue) {
|
||||
public void run(User user, List<ObjectNode> userInputs, SseEmitter sseEmitter, Long userId, String tokenValue, Long sessionId) {
|
||||
this.user = user;
|
||||
this.sseEmitter = sseEmitter;
|
||||
log.info("WorkflowEngine run,userId:{},workflowUuid:{},userInputs:{}", user.getId(), workflow.getUuid(), userInputs);
|
||||
@@ -86,7 +92,7 @@ public class WorkflowEngine {
|
||||
Pair<WorkflowNode, Set<WorkflowNode>> startAndEnds = findStartAndEndNode();
|
||||
WorkflowNode startNode = startAndEnds.getLeft();
|
||||
List<NodeIOData> wfInputs = getAndCheckUserInput(userInputs, startNode);
|
||||
this.wfState = new WfState(user, wfInputs, runtimeUuid,userId, tokenValue, sseEmitter);
|
||||
this.wfState = new WfState(user, wfInputs, runtimeUuid,userId, tokenValue, sseEmitter, sessionId);
|
||||
workflowRuntimeService.updateInput(this.wfRuntimeResp.getId(), wfState);
|
||||
|
||||
|
||||
@@ -122,6 +128,8 @@ public class WorkflowEngine {
|
||||
String intTip = WorkflowUtil.getHumanFeedbackTip(nextNode, wfNodes);
|
||||
//将等待输入信息[事件与提示词]发送到到客户端
|
||||
SSEEmitterHelper.parseAndSendPartialMsg(sseEmitter, "[NODE_WAIT_FEEDBACK_BY_" + nextNode + "]", intTip);
|
||||
// 保存提示信息到Chat信息记录中(对话使用)
|
||||
WorkflowMessageUtil.saveWorkflowMessage(wfState, intTip);
|
||||
InterruptedFlow.RUNTIME_TO_GRAPH.put(wfState.getUuid(), this);
|
||||
//更新状<E696B0>?
|
||||
wfState.setProcessStatus(WORKFLOW_PROCESS_STATUS_WAITING_INPUT);
|
||||
@@ -241,6 +249,7 @@ public class WorkflowEngine {
|
||||
Map<String, String> strMap = new HashMap<>();
|
||||
strMap.put("ck", chunk);
|
||||
// SSEEmitterHelper.parseAndSendPartialMsg(sseEmitter, "[NODE_CHUNK_" + node + "]", strMap.toString());
|
||||
|
||||
SSEEmitterHelper.parseAndSendPartialMsg(sseEmitter, "[NODE_CHUNK_" + node + "]", chunk);
|
||||
} else {
|
||||
AbstractWfNode abstractWfNode = wfState.getCompletedNodes().stream()
|
||||
@@ -349,8 +358,4 @@ public class WorkflowEngine {
|
||||
return Pair.of(startNode, endNodes);
|
||||
}
|
||||
|
||||
|
||||
public CompiledGraph<WfNodeState> getApp() {
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.bsc.langgraph4j.GraphStateException;
|
||||
import org.bsc.langgraph4j.StateGraph;
|
||||
import org.bsc.langgraph4j.serializer.std.ObjectStreamStateSerializer;
|
||||
import org.ruoyi.common.chat.enums.ErrorEnum;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||
import org.ruoyi.workflow.entity.WorkflowEdge;
|
||||
import org.ruoyi.workflow.entity.WorkflowNode;
|
||||
import org.ruoyi.workflow.enums.ErrorEnum;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -4,6 +4,8 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.entity.User;
|
||||
import org.ruoyi.common.chat.service.workFlow.IWorkFlowStarterService;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
import org.ruoyi.common.sse.core.SseEmitterManager;
|
||||
@@ -12,17 +14,16 @@ import org.ruoyi.workflow.helper.SSEEmitterHelper;
|
||||
import org.ruoyi.workflow.service.*;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.ruoyi.workflow.cosntant.AdiConstant.SSE_TIMEOUT;
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.*;
|
||||
import static org.ruoyi.common.chat.enums.ErrorEnum.*;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WorkflowStarter {
|
||||
@Service
|
||||
public class WorkflowStarter implements IWorkFlowStarterService {
|
||||
|
||||
@Lazy
|
||||
@Resource
|
||||
@@ -52,8 +53,7 @@ public class WorkflowStarter {
|
||||
@Resource
|
||||
private SseEmitterManager sseEmitterManager;
|
||||
|
||||
|
||||
public SseEmitter streaming(User user, String workflowUuid, List<ObjectNode> userInputs) {
|
||||
public SseEmitter streaming(User user, String workflowUuid, List<ObjectNode> userInputs, Long sessionId) {
|
||||
// 获取用户ID
|
||||
Long userId = LoginHelper.getUserId();
|
||||
// 获取登录Token
|
||||
@@ -71,12 +71,12 @@ public class WorkflowStarter {
|
||||
sseEmitterHelper.sendErrorAndComplete(user.getId(), sseEmitter, A_WF_DISABLED.getInfo());
|
||||
return sseEmitter;
|
||||
}
|
||||
self.asyncRun(user, workflow, userInputs, sseEmitter, userId, tokenValue);
|
||||
self.asyncRun(user, workflow, userInputs, sseEmitter, userId, tokenValue, sessionId);
|
||||
return sseEmitter;
|
||||
}
|
||||
|
||||
@Async
|
||||
public void asyncRun(User user, Workflow workflow, List<ObjectNode> userInputs, SseEmitter sseEmitter, Long userId, String tokenValue) {
|
||||
public void asyncRun(User user, Workflow workflow, List<ObjectNode> userInputs, SseEmitter sseEmitter, Long userId, String tokenValue, Long sessionId) {
|
||||
log.info("WorkflowEngine run,userId:{},workflowUuid:{},userInputs:{}", user.getId(), workflow.getUuid(), userInputs);
|
||||
List<WorkflowComponent> components = workflowComponentService.getAllEnable();
|
||||
List<WorkflowNode> nodes = workflowNodeService.lambdaQuery()
|
||||
@@ -90,17 +90,20 @@ public class WorkflowStarter {
|
||||
WorkflowEngine workflowEngine = new WorkflowEngine(workflow,
|
||||
sseEmitterHelper, components, nodes, edges,
|
||||
workflowRuntimeService, workflowRuntimeNodeService);
|
||||
workflowEngine.run(user, userInputs, sseEmitter, userId, tokenValue);
|
||||
workflowEngine.run(user, userInputs, sseEmitter, userId, tokenValue, sessionId);
|
||||
}
|
||||
|
||||
@Async
|
||||
public void resumeFlow(String runtimeUuid, String userInput) {
|
||||
public void resumeFlow(String runtimeUuid, String userInput, SseEmitter sseEmitter) {
|
||||
WorkflowEngine workflowEngine = InterruptedFlow.RUNTIME_TO_GRAPH.get(runtimeUuid);
|
||||
if (null == workflowEngine) {
|
||||
log.error("工作流恢复执行时失败,runtime:{}", runtimeUuid);
|
||||
throw new BaseException(A_WF_RESUME_FAIL.getInfo());
|
||||
}
|
||||
// 如果SSE连接对象不为空传入该对象(Chat调用工作流对话使用)
|
||||
if (null != sseEmitter){
|
||||
workflowEngine.setSseEmitter(sseEmitter);
|
||||
}
|
||||
workflowEngine.resume(userInput);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,12 +11,14 @@ import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bsc.langgraph4j.langchain4j.generators.StreamingChatGenerator;
|
||||
import org.bsc.langgraph4j.state.AgentState;
|
||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||
import org.ruoyi.common.chat.Service.IChatService;
|
||||
import org.ruoyi.common.chat.Service.IImageGenerationService;
|
||||
import org.ruoyi.common.chat.enums.RoleType;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.common.chat.service.chat.IChatService;
|
||||
import org.ruoyi.common.chat.service.chatMessage.AbstractChatMessageService;
|
||||
import org.ruoyi.common.chat.service.image.IImageGenerationService;
|
||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||
import org.ruoyi.common.chat.domain.entity.image.ImageContext;
|
||||
import org.ruoyi.common.chat.entity.chat.ChatContext;
|
||||
import org.ruoyi.common.chat.entity.image.ImageContext;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
||||
import org.ruoyi.common.chat.factory.ImageServiceFactory;
|
||||
@@ -28,6 +30,7 @@ import org.ruoyi.workflow.workflow.data.NodeIOData;
|
||||
import org.ruoyi.workflow.workflow.data.NodeIODataContent;
|
||||
import org.ruoyi.workflow.workflow.def.WfNodeParamRef;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import java.util.*;
|
||||
@@ -35,8 +38,8 @@ import java.util.*;
|
||||
import static org.ruoyi.workflow.cosntant.AdiConstant.WorkflowConstant.DEFAULT_OUTPUT_PARAM_NAME;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WorkflowUtil {
|
||||
@Service
|
||||
public class WorkflowUtil extends AbstractChatMessageService {
|
||||
|
||||
@Resource
|
||||
private ChatServiceFactory chatServiceFactory;
|
||||
@@ -123,11 +126,38 @@ public class WorkflowUtil {
|
||||
// 根据 category 获取对应的 ChatService(不使用计费代理,工作流场景单独计费)
|
||||
IChatService chatService = chatServiceFactory.getOriginalService(category);
|
||||
|
||||
// 获取用户信息和Token以及SSe连接对象(对话接口需要使用)
|
||||
Long sessionId = wfState.getSessionId();
|
||||
Long userId = wfState.getUserId();
|
||||
String tokenValue = wfState.getTokenValue();
|
||||
SseEmitter sseEmitter = wfState.getSseEmitter();
|
||||
|
||||
// 构建 ruoyi-ai 的 ChatRequest
|
||||
List<ChatMessage> chatMessages = new ArrayList<>();
|
||||
addUserMessage(node, state.getInputs(), chatMessages);
|
||||
chatMessages.addAll(systemMessage);
|
||||
|
||||
// 定义模型调用对象
|
||||
ChatRequest chatRequest = new ChatRequest();
|
||||
// 目前工作流深度思考成员变量只能写死
|
||||
chatRequest.setSessionId(sessionId);
|
||||
chatRequest.setEnableThinking(false);
|
||||
chatRequest.setModel(modelName);
|
||||
chatRequest.setChatMessages(chatMessages);
|
||||
|
||||
// 构建流式生成器
|
||||
StreamingChatGenerator<AgentState> streamingGenerator = StreamingChatGenerator.builder()
|
||||
.mapResult(response -> {
|
||||
String responseTxt = response.aiMessage().text();
|
||||
log.info("llm response:{}", responseTxt);
|
||||
|
||||
// 会话ID不为空时插入数据库
|
||||
if (sessionId != null){
|
||||
// 保存助手回复消息
|
||||
saveChatMessage(chatRequest, userId, responseTxt, RoleType.ASSISTANT.getName(), chatModelVo);
|
||||
log.info("{}消息结束,已保存到数据库", getProviderName());
|
||||
}
|
||||
|
||||
// 传递所有输入数据 + 添加 LLM 输出
|
||||
wfState.getNodeStateByNodeUuid(node.getUuid()).ifPresent(item -> {
|
||||
List<NodeIOData> outputs = new ArrayList<>(item.getInputs());
|
||||
@@ -142,24 +172,9 @@ public class WorkflowUtil {
|
||||
.startingState(state)
|
||||
.build();
|
||||
|
||||
// 获取用户信息和Token以及SSe连接对象(对话接口需要使用)
|
||||
Long userId = wfState.getUserId();
|
||||
String tokenValue = wfState.getTokenValue();
|
||||
SseEmitter sseEmitter = wfState.getSseEmitter();
|
||||
// 构建流式回调响应器
|
||||
StreamingChatResponseHandler handler = streamingGenerator.handler();
|
||||
|
||||
// 构建 ruoyi-ai 的 ChatRequest
|
||||
List<ChatMessage> chatMessages = new ArrayList<>();
|
||||
addUserMessage(node, state.getInputs(), chatMessages);
|
||||
chatMessages.addAll(systemMessage);
|
||||
|
||||
// 定义模型调用对象
|
||||
ChatRequest chatRequest = new ChatRequest();
|
||||
// 目前工作流深度思考成员变量只能写死
|
||||
chatRequest.setEnableThinking(false);
|
||||
chatRequest.setModel(modelName);
|
||||
chatRequest.setChatMessages(chatMessages);
|
||||
|
||||
//构建聊天对话上下文参数
|
||||
ChatContext chatContext = ChatContext.builder()
|
||||
.chatModelVo(chatModelVo)
|
||||
@@ -231,9 +246,9 @@ public class WorkflowUtil {
|
||||
throw new IllegalArgumentException("模型不存在: " + modelName);
|
||||
}
|
||||
// 根据模型名称找到模型实体
|
||||
String modelVoCategory = chatModelVo.getCategory();
|
||||
String category = chatModelVo.getProviderCode();
|
||||
// 根据 category 获取对应的 IImageGenerationService(不使用计费代理,工作流场景单独计费)
|
||||
IImageGenerationService imageService = imageServiceFactory.getOriginalService(modelVoCategory);
|
||||
IImageGenerationService imageService = imageServiceFactory.getOriginalService(category);
|
||||
// 构建文生图上下文对象
|
||||
ImageContext imageContext = ImageContext.builder()
|
||||
.chatModelVo(chatModelVo)
|
||||
@@ -244,5 +259,4 @@ public class WorkflowUtil {
|
||||
// 调用LLM 生成图片
|
||||
return imageService.generateImage(imageContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,18 +6,19 @@ import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.enums.RoleType;
|
||||
import org.ruoyi.common.core.exception.base.BaseException;
|
||||
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
||||
import org.ruoyi.workflow.entity.WorkflowComponent;
|
||||
import org.ruoyi.workflow.entity.WorkflowNode;
|
||||
import org.ruoyi.workflow.enums.WfIODataTypeEnum;
|
||||
import org.ruoyi.workflow.helper.SSEEmitterHelper;
|
||||
import org.ruoyi.workflow.util.JsonUtil;
|
||||
import org.ruoyi.workflow.util.SpringUtil;
|
||||
import org.ruoyi.workflow.workflow.NodeProcessResult;
|
||||
import org.ruoyi.workflow.workflow.WfNodeInputConfig;
|
||||
import org.ruoyi.workflow.workflow.WfNodeState;
|
||||
import org.ruoyi.workflow.workflow.WfState;
|
||||
import org.ruoyi.workflow.util.WorkflowMessageUtil;
|
||||
import org.ruoyi.workflow.workflow.*;
|
||||
import org.ruoyi.workflow.workflow.data.NodeIOData;
|
||||
import org.ruoyi.workflow.workflow.def.WfNodeIO;
|
||||
import org.ruoyi.workflow.workflow.def.WfNodeParamRef;
|
||||
@@ -31,8 +32,8 @@ import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.ruoyi.workflow.cosntant.AdiConstant.WorkflowConstant.*;
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.A_WF_NODE_CONFIG_ERROR;
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.A_WF_NODE_CONFIG_NOT_FOUND;
|
||||
import static org.ruoyi.common.chat.enums.ErrorEnum.A_WF_NODE_CONFIG_ERROR;
|
||||
import static org.ruoyi.common.chat.enums.ErrorEnum.A_WF_NODE_CONFIG_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* 节点实例-运行时
|
||||
@@ -225,4 +226,19 @@ public abstract class AbstractWfNode {
|
||||
return nodeConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话消息保存方法
|
||||
*/
|
||||
public void saveSessionMessage(WfState wfState, String message) {
|
||||
WorkflowMessageUtil.saveWorkflowMessage(wfState, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送SSe消息
|
||||
* @param message 信息
|
||||
*/
|
||||
public void sendSseEvent(String message){
|
||||
String nodeUuid = node.getUuid();
|
||||
SSEEmitterHelper.parseAndSendPartialMsg(wfState.getSseEmitter(), "[NODE_CHUNK_" + nodeUuid + "]", message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,11 @@ public class HttpRequestNode extends AbstractWfNode {
|
||||
List<NodeIOData> outputs = new ArrayList<>();
|
||||
outputs.add(NodeIOData.createByText("output", "HTTP响应", response));
|
||||
|
||||
// 保存成功会话信息
|
||||
String message = "HTTP响应:" + response;
|
||||
saveSessionMessage(wfState, message);
|
||||
// 发送驱动消息事件
|
||||
sendSseEvent(message);
|
||||
return NodeProcessResult.builder().content(outputs).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
@@ -73,6 +78,11 @@ public class HttpRequestNode extends AbstractWfNode {
|
||||
errorOutputs.add(NodeIOData.createByText("output", "错误", ""));
|
||||
errorOutputs.add(NodeIOData.createByText("error", "HTTP请求错误", e.getMessage()));
|
||||
|
||||
// 保存失败会话信息
|
||||
String message = "HTTP响应失败:" + e.getMessage();
|
||||
saveSessionMessage(wfState, message);
|
||||
// 发送驱动消息事件
|
||||
sendSseEvent(message);
|
||||
return NodeProcessResult.builder().content(errorOutputs).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.ruoyi.workflow.entity.WorkflowNode;
|
||||
import org.ruoyi.workflow.workflow.NodeProcessResult;
|
||||
import org.ruoyi.workflow.workflow.WfNodeState;
|
||||
import org.ruoyi.workflow.workflow.WfState;
|
||||
import org.ruoyi.workflow.workflow.WorkflowUtil;
|
||||
import org.ruoyi.workflow.workflow.data.NodeIOData;
|
||||
import org.ruoyi.workflow.workflow.node.AbstractWfNode;
|
||||
|
||||
|
||||
@@ -51,6 +51,11 @@ public class ImageNode extends AbstractWfNode {
|
||||
Integer seed = nodeConfigObj.getSeed();
|
||||
// 调用LLM生成图片(后续可以将图片保存到OSS中)
|
||||
String imageUrl = workflowUtil.buildTextToImage(modelName, prompt, size, seed);
|
||||
// 保存成功信息
|
||||
String message = "图片生成地址:" + imageUrl;
|
||||
saveSessionMessage(wfState, message);
|
||||
// 发送驱动消息事件
|
||||
sendSseEvent(message);
|
||||
// 创建节点参数对象
|
||||
NodeIOData nodeIOData = NodeIOData.createByText("output", "image", imageUrl);
|
||||
// 添加到输出列表以便给后续节点使用
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.ruoyi.workflow.workflow.node.mailSend;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.JSONValidator;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -31,6 +33,12 @@ public class MailSendNode extends AbstractWfNode {
|
||||
try {
|
||||
MailSendNodeConfig config = checkAndGetConfig(MailSendNodeConfig.class);
|
||||
List<NodeIOData> inputs = state.getInputs();
|
||||
// 获取输入信息
|
||||
String input = getDataFromInput(inputs);
|
||||
// 判断是否为JSON格式(LLM输出转换 由LLM生成格式)
|
||||
if (StringUtils.isNotBlank(input) && isJson(input)) {
|
||||
config = JSONObject.parseObject(input, MailSendNodeConfig.class);
|
||||
}
|
||||
|
||||
// 安全获取模板(使用 defaultString 避免 null)
|
||||
String subjectTemplate = StringUtils.defaultString(config.getSubject());
|
||||
@@ -49,15 +57,7 @@ public class MailSendNode extends AbstractWfNode {
|
||||
content = WorkflowUtil.renderTemplate(contentTemplate, inputs);
|
||||
} else {
|
||||
// 优先使用 output,如果没有则使用 input
|
||||
content = inputs.stream()
|
||||
.filter(item -> "output".equals(item.getName()))
|
||||
.map(NodeIOData::valueToString)
|
||||
.findFirst()
|
||||
.orElseGet(() -> inputs.stream()
|
||||
.filter(item -> "input".equals(item.getName()))
|
||||
.map(NodeIOData::valueToString)
|
||||
.findFirst()
|
||||
.orElse(""));
|
||||
content = getDataFromInput(inputs);
|
||||
}
|
||||
|
||||
// 将换行符转换为 HTML 换行
|
||||
@@ -84,9 +84,9 @@ public class MailSendNode extends AbstractWfNode {
|
||||
|
||||
// 设置收件人
|
||||
String[] toArray = Arrays.stream(toMails.split(","))
|
||||
.map(String::trim)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.toArray(String[]::new);
|
||||
.map(String::trim)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.toArray(String[]::new);
|
||||
if (toArray.length == 0) {
|
||||
throw new IllegalArgumentException("收件人邮箱列表为空");
|
||||
}
|
||||
@@ -95,9 +95,9 @@ public class MailSendNode extends AbstractWfNode {
|
||||
// 设置抄送(如有)
|
||||
if (StringUtils.isNotBlank(ccMails)) {
|
||||
String[] ccArray = Arrays.stream(ccMails.split(","))
|
||||
.map(String::trim)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.toArray(String[]::new);
|
||||
.map(String::trim)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.toArray(String[]::new);
|
||||
if (ccArray.length > 0) {
|
||||
helper.setCc(ccArray);
|
||||
}
|
||||
@@ -111,25 +111,31 @@ public class MailSendNode extends AbstractWfNode {
|
||||
mailSender.send(message);
|
||||
log.info("Email sent successfully to: {}", toMails);
|
||||
|
||||
// 保存成功会话信息
|
||||
String resultMessage = "发送邮箱成功";
|
||||
saveSessionMessage(wfState, resultMessage);
|
||||
// 发送驱动消息事件
|
||||
sendSseEvent(resultMessage);
|
||||
|
||||
// 构造输出:统一输出为 output 参数
|
||||
List<NodeIOData> outputs = new java.util.ArrayList<>();
|
||||
|
||||
// 优先使用 output,如果没有则使用 input(但重命名为 output)
|
||||
inputs.stream()
|
||||
.filter(item -> "output".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresentOrElse(
|
||||
outputs::add,
|
||||
() -> inputs.stream()
|
||||
.filter(item -> "input".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresent(inputParam -> {
|
||||
String title = inputParam.getContent() != null && inputParam.getContent().getTitle() != null
|
||||
? inputParam.getContent().getTitle() : "";
|
||||
NodeIOData outputParam = NodeIOData.createByText("output", title, inputParam.valueToString());
|
||||
outputs.add(outputParam);
|
||||
})
|
||||
);
|
||||
.filter(item -> "output".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresentOrElse(
|
||||
outputs::add,
|
||||
() -> inputs.stream()
|
||||
.filter(item -> "input".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresent(inputParam -> {
|
||||
String title = inputParam.getContent() != null && inputParam.getContent().getTitle() != null
|
||||
? inputParam.getContent().getTitle() : "";
|
||||
NodeIOData outputParam = NodeIOData.createByText("output", title, resultMessage);
|
||||
outputs.add(outputParam);
|
||||
})
|
||||
);
|
||||
|
||||
return NodeProcessResult.builder().content(outputs).build();
|
||||
|
||||
@@ -138,23 +144,29 @@ public class MailSendNode extends AbstractWfNode {
|
||||
// 异常时也统一输出为 output 参数,添加错误信息
|
||||
List<NodeIOData> errorOutputs = new java.util.ArrayList<>();
|
||||
|
||||
state.getInputs().stream()
|
||||
.filter(item -> "output".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresentOrElse(
|
||||
errorOutputs::add,
|
||||
() -> state.getInputs().stream()
|
||||
.filter(item -> "input".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresent(inputParam -> {
|
||||
String title = inputParam.getContent() != null && inputParam.getContent().getTitle() != null
|
||||
? inputParam.getContent().getTitle() : "";
|
||||
NodeIOData outputParam = NodeIOData.createByText("output", title, inputParam.valueToString());
|
||||
errorOutputs.add(outputParam);
|
||||
})
|
||||
);
|
||||
// 保存失败会话信息
|
||||
String resultMessage = "发送邮箱失败: " + e.getMessage();
|
||||
saveSessionMessage(wfState, resultMessage);
|
||||
// 发送驱动消息事件
|
||||
sendSseEvent(resultMessage);
|
||||
|
||||
errorOutputs.add(NodeIOData.createByText("error", "mail", e.getMessage()));
|
||||
state.getInputs().stream()
|
||||
.filter(item -> "output".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresentOrElse(
|
||||
errorOutputs::add,
|
||||
() -> state.getInputs().stream()
|
||||
.filter(item -> "input".equals(item.getName()))
|
||||
.findFirst()
|
||||
.ifPresent(inputParam -> {
|
||||
String title = inputParam.getContent() != null && inputParam.getContent().getTitle() != null
|
||||
? inputParam.getContent().getTitle() : "";
|
||||
NodeIOData outputParam = NodeIOData.createByText("output", title, resultMessage);
|
||||
errorOutputs.add(outputParam);
|
||||
})
|
||||
);
|
||||
|
||||
errorOutputs.add(NodeIOData.createByText("error", "mail", resultMessage));
|
||||
return NodeProcessResult.builder().content(errorOutputs).build();
|
||||
}
|
||||
}
|
||||
@@ -174,4 +186,40 @@ public class MailSendNode extends AbstractWfNode {
|
||||
|
||||
return sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取信息
|
||||
* @param inputs 用户输入
|
||||
* @return 返回输入信息
|
||||
*/
|
||||
public String getDataFromInput(List<NodeIOData> inputs) {
|
||||
return inputs.stream()
|
||||
.filter(item -> "output".equals(item.getName()))
|
||||
.map(NodeIOData::valueToString)
|
||||
.findFirst()
|
||||
.orElseGet(() -> inputs.stream()
|
||||
.filter(item -> "input".equals(item.getName()))
|
||||
.map(NodeIOData::valueToString)
|
||||
.findFirst()
|
||||
.orElse(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为合法的 JSON 格式
|
||||
*
|
||||
* @param str 待检测的字符串
|
||||
* @return true 表示是合法 JSON (包括 JSONObject, JSONArray, 或基本类型值)
|
||||
*/
|
||||
public static boolean isJson(String str) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 使用 try-with-resources 正确处理 JSONValidator 资源关闭
|
||||
try (JSONValidator validator = JSONValidator.from(str.trim())) {
|
||||
return validator.getType() == JSONValidator.Type.Object;
|
||||
} catch (Exception e) {
|
||||
log.warn("JSON格式校验失败: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ import org.ruoyi.workflow.workflow.node.AbstractWfNode;
|
||||
import java.util.List;
|
||||
|
||||
import static org.ruoyi.workflow.cosntant.AdiConstant.WorkflowConstant.DEFAULT_OUTPUT_PARAM_NAME;
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.A_WF_NODE_CONFIG_ERROR;
|
||||
import static org.ruoyi.workflow.enums.ErrorEnum.A_WF_NODE_CONFIG_NOT_FOUND;
|
||||
import static org.ruoyi.common.chat.enums.ErrorEnum.A_WF_NODE_CONFIG_ERROR;
|
||||
import static org.ruoyi.common.chat.enums.ErrorEnum.A_WF_NODE_CONFIG_NOT_FOUND;
|
||||
|
||||
@Slf4j
|
||||
public class StartNode extends AbstractWfNode {
|
||||
|
||||
@@ -6,7 +6,9 @@ import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.service.chat.IChatMessageService;
|
||||
import org.ruoyi.common.chat.domain.bo.chat.ChatMessageBo;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatMessageVo;
|
||||
import org.ruoyi.common.chat.service.chatMessage.IChatMessageService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
||||
@@ -18,8 +20,6 @@ import org.ruoyi.common.core.validate.AddGroup;
|
||||
import org.ruoyi.common.core.validate.EditGroup;
|
||||
import org.ruoyi.common.log.enums.BusinessType;
|
||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
||||
import org.ruoyi.domain.vo.chat.ChatMessageVo;
|
||||
import org.ruoyi.domain.bo.chat.ChatMessageBo;
|
||||
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||
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.ModelType;
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.ruoyi.domain.bo.chat;
|
||||
|
||||
import org.ruoyi.common.core.validate.AddGroup;
|
||||
import org.ruoyi.common.core.validate.EditGroup;
|
||||
import org.ruoyi.domain.entity.chat.ChatMessage;
|
||||
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 聊天消息业务对象 chat_message
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-12-14
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = ChatMessage.class, reverseConvertGenerate = false)
|
||||
public class ChatMessageBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 会话id
|
||||
*/
|
||||
private Long sessionId;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@NotNull(message = "用户id不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 对话角色
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 扣除金额
|
||||
*/
|
||||
private Long deductCost;
|
||||
|
||||
/**
|
||||
* 累计 Tokens
|
||||
*/
|
||||
private Long totalTokens;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 计费类型(1-token计费,2-次数计费)
|
||||
*/
|
||||
private String billingType;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.ruoyi.domain.entity.chat;
|
||||
|
||||
import org.ruoyi.common.tenant.core.TenantEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 聊天消息对象 chat_message
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-12-14
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("chat_message")
|
||||
public class ChatMessage extends TenantEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 会话id
|
||||
*/
|
||||
private Long sessionId;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 对话角色
|
||||
*/
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 扣除金额
|
||||
*/
|
||||
private Long deductCost;
|
||||
|
||||
/**
|
||||
* 累计 Tokens
|
||||
*/
|
||||
private Long totalTokens;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 计费类型(1-token计费,2-次数计费)
|
||||
*/
|
||||
private String billingType;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package org.ruoyi.domain.vo.chat;
|
||||
|
||||
import org.ruoyi.domain.entity.chat.ChatMessage;
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import org.ruoyi.common.excel.annotation.ExcelDictFormat;
|
||||
import org.ruoyi.common.excel.convert.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 聊天消息视图对象 chat_message
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-12-14
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = ChatMessage.class)
|
||||
public class ChatMessageVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@ExcelProperty(value = "主键")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 会话id
|
||||
*/
|
||||
@ExcelProperty(value = "会话id")
|
||||
private Long sessionId;
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
@ExcelProperty(value = "用户id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
@ExcelProperty(value = "消息内容")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 对话角色
|
||||
*/
|
||||
@ExcelProperty(value = "对话角色")
|
||||
private String role;
|
||||
|
||||
/**
|
||||
* 扣除金额
|
||||
*/
|
||||
@ExcelProperty(value = "扣除金额")
|
||||
private Long deductCost;
|
||||
|
||||
/**
|
||||
* 累计 Tokens
|
||||
*/
|
||||
@ExcelProperty(value = "累计 Tokens")
|
||||
private Long totalTokens;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
@ExcelProperty(value = "模型名称")
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 计费类型(1-token计费,2-次数计费)
|
||||
*/
|
||||
@ExcelProperty(value = "计费类型", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "1=-token计费,2-次数计费")
|
||||
private String billingType;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.ruoyi.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 角色枚举
|
||||
*
|
||||
* @author ageerle@163.com
|
||||
* @date 2025-12-17
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum RoleType {
|
||||
|
||||
SYSTEM("system"),
|
||||
USER("user"),
|
||||
ASSISTANT("assistant"),
|
||||
FUNCTION("function"),
|
||||
TOOL("tool"),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package org.ruoyi.factory;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.service.embed.BaseEmbedModelService;
|
||||
import org.ruoyi.service.embed.MultiModalEmbedModelService;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.ruoyi.mapper.chat;
|
||||
|
||||
import org.ruoyi.domain.entity.chat.ChatMessage;
|
||||
import org.ruoyi.domain.vo.chat.ChatMessageVo;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatMessageVo;
|
||||
import org.ruoyi.common.chat.entity.chat.ChatMessage;
|
||||
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.ruoyi.mapper.chat;
|
||||
|
||||
import org.ruoyi.common.chat.domain.entity.chat.ChatModel;
|
||||
import org.ruoyi.common.chat.entity.chat.ChatModel;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package org.ruoyi.service.chat;
|
||||
|
||||
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
||||
import org.ruoyi.common.mybatis.core.page.PageQuery;
|
||||
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
||||
import org.ruoyi.domain.bo.chat.ChatMessageBo;
|
||||
import org.ruoyi.domain.vo.chat.ChatMessageVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 聊天消息Service接口
|
||||
*
|
||||
* @author ageerle
|
||||
* @date 2025-12-14
|
||||
*/
|
||||
public interface IChatMessageService {
|
||||
|
||||
/**
|
||||
* 查询聊天消息
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 聊天消息
|
||||
*/
|
||||
ChatMessageVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询聊天消息列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 聊天消息分页列表
|
||||
*/
|
||||
TableDataInfo<ChatMessageVo> queryPageList(ChatMessageBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的聊天消息列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 聊天消息列表
|
||||
*/
|
||||
List<ChatMessageVo> queryList(ChatMessageBo bo);
|
||||
|
||||
/**
|
||||
* 新增聊天消息
|
||||
*
|
||||
* @param bo 聊天消息
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(ChatMessageBo bo);
|
||||
|
||||
/**
|
||||
* 修改聊天消息
|
||||
*
|
||||
* @param bo 聊天消息
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(ChatMessageBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除聊天消息信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 根据会话ID获取所有消息
|
||||
* 用于长期记忆功能
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 消息DTO列表
|
||||
*/
|
||||
List<ChatMessageDTO> getMessagesBySessionId(Long sessionId);
|
||||
|
||||
/**
|
||||
* 根据会话ID删除所有消息
|
||||
* 用于清理会话历史
|
||||
*
|
||||
* @param sessionId 会话ID
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteBySessionId(Long sessionId);
|
||||
}
|
||||
@@ -24,17 +24,20 @@ import org.ruoyi.agent.WebSearchAgent;
|
||||
import org.ruoyi.agent.tool.ExecuteSqlQueryTool;
|
||||
import org.ruoyi.agent.tool.QueryAllTablesTool;
|
||||
import org.ruoyi.agent.tool.QueryTableSchemaTool;
|
||||
import org.ruoyi.common.chat.Service.IChatService;
|
||||
import org.ruoyi.common.chat.base.ThreadContext;
|
||||
import org.ruoyi.common.chat.domain.dto.request.ReSumeRunner;
|
||||
import org.ruoyi.common.chat.domain.dto.request.WorkFlowRunner;
|
||||
import org.ruoyi.common.chat.enums.RoleType;
|
||||
import org.ruoyi.common.chat.service.chat.IChatService;
|
||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||
import org.ruoyi.common.chat.entity.chat.ChatContext;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.service.chatMessage.AbstractChatMessageService;
|
||||
import org.ruoyi.common.chat.service.workFlow.IWorkFlowStarterService;
|
||||
import org.ruoyi.common.core.utils.ObjectUtils;
|
||||
import org.ruoyi.common.core.utils.SpringUtils;
|
||||
import org.ruoyi.common.core.utils.StringUtils;
|
||||
import org.ruoyi.common.sse.utils.SseMessageUtils;
|
||||
import org.ruoyi.domain.bo.chat.ChatMessageBo;
|
||||
import org.ruoyi.enums.RoleType;
|
||||
import org.ruoyi.service.chat.IChatMessageService;
|
||||
import org.ruoyi.service.chat.impl.memory.PersistentChatMemoryStore;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -58,7 +61,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
@Slf4j
|
||||
@Validated
|
||||
public abstract class AbstractStreamingChatService implements IChatService {
|
||||
public abstract class AbstractStreamingChatService extends AbstractChatMessageService implements IChatService {
|
||||
|
||||
/**
|
||||
* 默认保留的消息窗口大小(用于长期记忆)
|
||||
@@ -76,6 +79,11 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
||||
*/
|
||||
private static final Map<Object, MessageWindowChatMemory> memoryCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取工作流启用Bean对象
|
||||
*/
|
||||
private static final IWorkFlowStarterService starterService = SpringUtils.getBean(IWorkFlowStarterService.class);
|
||||
|
||||
/**
|
||||
* 定义聊天流程骨架
|
||||
*/
|
||||
@@ -108,9 +116,28 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
||||
|
||||
// 保存用户消息
|
||||
saveChatMessage(chatRequest, userId, content, RoleType.USER.getName(), chatModelVo);
|
||||
|
||||
// 判断用户是否重新输入
|
||||
boolean isResume = chatRequest.getIsResume() != null && chatRequest.getIsResume();
|
||||
if (isResume){
|
||||
ReSumeRunner reSumeRunner = chatRequest.getReSumeRunner();
|
||||
if (ObjectUtils.isNotEmpty(reSumeRunner)){
|
||||
starterService.resumeFlow(reSumeRunner.getRuntimeUuid(), reSumeRunner.getFeedbackContent(), emitter);
|
||||
return emitter;
|
||||
}
|
||||
}
|
||||
|
||||
// 判断用户是否开启工作流
|
||||
boolean enableWorkFlow = chatRequest.getEnableWorkFlow() != null && chatRequest.getEnableWorkFlow();
|
||||
if (enableWorkFlow) {
|
||||
WorkFlowRunner runner = chatRequest.getWorkFlowRunner();
|
||||
if (ObjectUtils.isNotEmpty(runner)){
|
||||
return starterService.streaming(ThreadContext.getCurrentUser(), runner.getUuid(), runner.getInputs(), chatRequest.getSessionId());
|
||||
}
|
||||
}
|
||||
|
||||
// 使用长期记忆增强的消息列表
|
||||
List<ChatMessage> messagesWithMemory = buildMessagesWithMemory(chatRequest);
|
||||
|
||||
if (chatRequest.getEnableThinking()) {
|
||||
String msg = doAgent(content, chatModelVo);
|
||||
SseMessageUtils.sendMessage(userId, msg);
|
||||
@@ -119,13 +146,10 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
||||
saveChatMessage(chatRequest, userId, msg, RoleType.ASSISTANT.getName(), chatModelVo);
|
||||
} else {
|
||||
// 创建包含内存管理的响应处理器
|
||||
if (ObjectUtils.isEmpty(handler)) {
|
||||
handler = createResponseHandler(chatRequest, userId, tokenValue, chatModelVo);
|
||||
}
|
||||
handler = ObjectUtils.isEmpty(handler) ? createResponseHandler(chatRequest, userId, tokenValue, chatModelVo) : handler;
|
||||
// 调用具体实现的聊天方法
|
||||
doChat(chatModelVo, chatRequest, messagesWithMemory, handler);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
SseMessageUtils.sendMessage(userId, "对话出错:" + e.getMessage());
|
||||
SseMessageUtils.completeConnection(userId, tokenValue);
|
||||
@@ -144,6 +168,12 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
||||
*/
|
||||
protected List<ChatMessage> buildMessagesWithMemory(ChatRequest chatRequest) {
|
||||
List<ChatMessage> messages = new ArrayList<>();
|
||||
// 工作流对话消息
|
||||
List<ChatMessage> chatMessages = chatRequest.getChatMessages();
|
||||
if (!CollectionUtils.isEmpty(chatMessages)){
|
||||
messages.addAll(chatMessages);
|
||||
}
|
||||
// 开启长期记忆
|
||||
if (enablePersistentMemory && chatRequest.getSessionId() != null) {
|
||||
MessageWindowChatMemory memory = createChatMemory(chatRequest.getSessionId());
|
||||
if (memory != null) {
|
||||
@@ -155,11 +185,6 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
// 工作流方式
|
||||
List<ChatMessage> chatMessages = chatRequest.getChatMessages();
|
||||
if (!CollectionUtils.isEmpty(chatMessages)){
|
||||
messages.addAll(chatMessages);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@@ -276,40 +301,6 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存聊天消息到数据库
|
||||
*
|
||||
* @param chatRequest 聊天请求
|
||||
* @param userId 用户ID
|
||||
* @param content 消息内容
|
||||
* @param role 消息角色
|
||||
* @param chatModelVo 模型配置
|
||||
*/
|
||||
private void saveChatMessage(ChatRequest chatRequest, Long userId, String content, String role, ChatModelVo chatModelVo) {
|
||||
try {
|
||||
// 验证必要的上下文信息
|
||||
if (chatRequest == null || userId == null) {
|
||||
log.warn("缺少必要的聊天上下文信息,无法保存消息");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建ChatMessageBo对象
|
||||
ChatMessageBo messageBO = new ChatMessageBo();
|
||||
messageBO.setUserId(userId);
|
||||
messageBO.setSessionId(chatRequest.getSessionId());
|
||||
messageBO.setContent(content);
|
||||
messageBO.setRole(role);
|
||||
messageBO.setModelName(chatRequest.getModel());
|
||||
messageBO.setBillingType(chatModelVo.getModelType());
|
||||
messageBO.setRemark(null);
|
||||
|
||||
IChatMessageService chatMessageService = SpringUtils.getBean(IChatMessageService.class);
|
||||
chatMessageService.insertByBo(messageBO);
|
||||
} catch (Exception e) {
|
||||
log.error("保存{}聊天消息时出错: {}", getProviderName(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建具体厂商的 StreamingChatModel
|
||||
* 子类必须实现此方法,返回对应厂商的模型实例
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package org.ruoyi.service.chat.impl;
|
||||
|
||||
import org.ruoyi.common.chat.domain.bo.chat.ChatMessageBo;
|
||||
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatMessageVo;
|
||||
import org.ruoyi.common.chat.entity.chat.ChatMessage;
|
||||
import org.ruoyi.common.chat.service.chatMessage.IChatMessageService;
|
||||
import org.ruoyi.common.core.utils.MapstructUtils;
|
||||
import org.ruoyi.common.core.utils.StringUtils;
|
||||
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
|
||||
@@ -10,11 +14,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.service.chat.IChatMessageService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.ruoyi.domain.bo.chat.ChatMessageBo;
|
||||
import org.ruoyi.domain.vo.chat.ChatMessageVo;
|
||||
import org.ruoyi.domain.entity.chat.ChatMessage;
|
||||
import org.ruoyi.mapper.chat.ChatMessageMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.ruoyi.service.chat.impl;
|
||||
|
||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo;
|
||||
import org.ruoyi.common.chat.domain.entity.chat.ChatModel;
|
||||
import org.ruoyi.common.chat.entity.chat.ChatModel;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.core.utils.MapstructUtils;
|
||||
import org.ruoyi.common.core.utils.StringUtils;
|
||||
|
||||
@@ -4,11 +4,11 @@ import cn.dev33.satoken.stp.StpUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||
import org.ruoyi.common.chat.Service.IChatService;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.common.chat.service.chat.IChatService;
|
||||
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||
import org.ruoyi.common.chat.entity.chat.ChatContext;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||
@@ -52,7 +52,6 @@ public class ChatServiceFacade {
|
||||
* @return SseEmitter
|
||||
*/
|
||||
public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
|
||||
|
||||
// 1. 根据模型名称查询完整配置
|
||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||
if (chatModelVo == null) {
|
||||
|
||||
@@ -108,7 +108,7 @@ public class ChatMemoryUsageExample {
|
||||
log.info("=== 示例4:清理过期消息 ===");
|
||||
/*
|
||||
// 假设已有IChatMessageService实例
|
||||
IChatMessageService chatMessageService = getBean(IChatMessageService.class);
|
||||
AbstractChatMessageService chatMessageService = getBean(AbstractChatMessageService.class);
|
||||
|
||||
// 场景:用户要求"忘记我们之前的对话"
|
||||
Long sessionId = 789L;
|
||||
|
||||
@@ -4,8 +4,8 @@ import dev.langchain4j.data.message.ChatMessage;
|
||||
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
||||
import org.ruoyi.common.chat.service.chatMessage.IChatMessageService;
|
||||
import org.ruoyi.common.core.utils.SpringUtils;
|
||||
import org.ruoyi.service.chat.IChatMessageService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.ruoyi.service.graph.impl;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||
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.config.GraphExtractPrompt;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.ruoyi.service.image;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.ruoyi.common.chat.Service.IImageGenerationService;
|
||||
import org.ruoyi.common.chat.service.image.IImageGenerationService;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.chat.domain.entity.image.ImageContext;
|
||||
import org.ruoyi.common.chat.entity.image.ImageContext;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@Slf4j
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.ruoyi.service.knowledge.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||
import org.ruoyi.common.chat.service.chat.IChatModelService;
|
||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||
import org.ruoyi.common.core.domain.dto.OssDTO;
|
||||
import org.ruoyi.common.core.service.OssService;
|
||||
|
||||
Reference in New Issue
Block a user