mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-13 03:45:14 +00:00
context:公共聊天接口调整为对话上下文对象传输
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
package org.ruoyi.common.chat.Service;
|
package org.ruoyi.common.chat.Service;
|
||||||
|
|
||||||
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
import jakarta.validation.Valid;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,12 +12,7 @@ public interface IChatService {
|
|||||||
/**
|
/**
|
||||||
* 客户端发送对话消息到服务端
|
* 客户端发送对话消息到服务端
|
||||||
*/
|
*/
|
||||||
SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue);
|
SseEmitter chat(@Valid ChatContext chatContext);
|
||||||
|
|
||||||
/**
|
|
||||||
* 工作流专用对话
|
|
||||||
*/
|
|
||||||
SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest,SseEmitter emitter,Long userId,String tokenValue, StreamingChatResponseHandler handler);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务提供商名称
|
* 获取服务提供商名称
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package org.ruoyi.common.chat.domain.entity.chat;
|
||||||
|
|
||||||
|
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聊天对话上下文对象
|
||||||
|
*
|
||||||
|
* @author zengxb
|
||||||
|
* @date 2026-02-14
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Builder
|
||||||
|
public class ChatContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型管理视图对象
|
||||||
|
*/
|
||||||
|
@NotNull(message = "模型管理视图对象不能为空")
|
||||||
|
private ChatModelVo chatModelVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话请求对象
|
||||||
|
*/
|
||||||
|
@NotNull(message = "对话请求对象不能为空")
|
||||||
|
private ChatRequest chatRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSe连接对象
|
||||||
|
*/
|
||||||
|
@NotNull(message = "SSe连接对象不能为空")
|
||||||
|
private SseEmitter emitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "用户ID不能为空")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token
|
||||||
|
*/
|
||||||
|
@NotNull(message = "Token不能为空")
|
||||||
|
private String tokenValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应处理器
|
||||||
|
*/
|
||||||
|
private StreamingChatResponseHandler handler;
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import org.bsc.langgraph4j.state.AgentState;
|
|||||||
import org.ruoyi.common.chat.Service.IChatModelService;
|
import org.ruoyi.common.chat.Service.IChatModelService;
|
||||||
import org.ruoyi.common.chat.Service.IChatService;
|
import org.ruoyi.common.chat.Service.IChatService;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
||||||
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
|
||||||
@@ -135,9 +136,11 @@ public class WorkflowUtil {
|
|||||||
.startingState(state)
|
.startingState(state)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// 获取用户信息和Token以及SSe连接对象(对话接口需要使用)
|
||||||
Long userId = wfState.getUserId();
|
Long userId = wfState.getUserId();
|
||||||
String tokenValue = wfState.getTokenValue();
|
String tokenValue = wfState.getTokenValue();
|
||||||
SseEmitter sseEmitter = wfState.getSseEmitter();
|
SseEmitter sseEmitter = wfState.getSseEmitter();
|
||||||
|
StreamingChatResponseHandler handler = streamingGenerator.handler();
|
||||||
|
|
||||||
// 构建 ruoyi-ai 的 ChatRequest
|
// 构建 ruoyi-ai 的 ChatRequest
|
||||||
List<ChatMessage> chatMessages = new ArrayList<>();
|
List<ChatMessage> chatMessages = new ArrayList<>();
|
||||||
@@ -151,9 +154,18 @@ public class WorkflowUtil {
|
|||||||
chatRequest.setModel(modelName);
|
chatRequest.setModel(modelName);
|
||||||
chatRequest.setChatMessages(chatMessages);
|
chatRequest.setChatMessages(chatMessages);
|
||||||
|
|
||||||
|
//构建聊天对话上下文参数
|
||||||
|
ChatContext chatContext = ChatContext.builder()
|
||||||
|
.chatModelVo(chatModelVo)
|
||||||
|
.chatRequest(chatRequest)
|
||||||
|
.emitter(sseEmitter)
|
||||||
|
.userId(userId)
|
||||||
|
.tokenValue(tokenValue)
|
||||||
|
.handler(handler)
|
||||||
|
.build();
|
||||||
|
|
||||||
// 使用工作流专用方法
|
// 使用工作流专用方法
|
||||||
StreamingChatResponseHandler handler = streamingGenerator.handler();
|
chatService.chat(chatContext);
|
||||||
chatService.chat(chatModelVo, chatRequest, sseEmitter, userId, tokenValue, handler);
|
|
||||||
wfState.getNodeToStreamingGenerator().put(node.getUuid(), streamingGenerator);
|
wfState.getNodeToStreamingGenerator().put(node.getUuid(), streamingGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.ruoyi.agent.tool.QueryAllTablesTool;
|
|||||||
import org.ruoyi.agent.tool.QueryTableSchemaTool;
|
import org.ruoyi.agent.tool.QueryTableSchemaTool;
|
||||||
import org.ruoyi.common.chat.Service.IChatService;
|
import org.ruoyi.common.chat.Service.IChatService;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
import org.ruoyi.common.core.utils.ObjectUtils;
|
import org.ruoyi.common.core.utils.ObjectUtils;
|
||||||
import org.ruoyi.common.core.utils.SpringUtils;
|
import org.ruoyi.common.core.utils.SpringUtils;
|
||||||
@@ -36,6 +37,7 @@ import org.ruoyi.enums.RoleType;
|
|||||||
import org.ruoyi.service.chat.IChatMessageService;
|
import org.ruoyi.service.chat.IChatMessageService;
|
||||||
import org.ruoyi.service.chat.impl.memory.PersistentChatMemoryStore;
|
import org.ruoyi.service.chat.impl.memory.PersistentChatMemoryStore;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -55,6 +57,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* @date 2025/12/13
|
* @date 2025/12/13
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@Validated
|
||||||
public abstract class AbstractStreamingChatService implements IChatService {
|
public abstract class AbstractStreamingChatService implements IChatService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,22 +80,19 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
|||||||
* 定义聊天流程骨架
|
* 定义聊天流程骨架
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue) {
|
public SseEmitter chat(ChatContext chatContext) {
|
||||||
return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, null);
|
// 获取模型管理视图对象
|
||||||
}
|
ChatModelVo chatModelVo = chatContext.getChatModelVo();
|
||||||
|
// 获取对话请求对象
|
||||||
/**
|
ChatRequest chatRequest = chatContext.getChatRequest();
|
||||||
* 定义聊天流程骨架(包含流式回调结构)
|
// 获取SSe连接对象
|
||||||
*/
|
SseEmitter emitter = chatContext.getEmitter();
|
||||||
@Override
|
// 获取用户ID
|
||||||
public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) {
|
Long userId = chatContext.getUserId();
|
||||||
return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, handler);
|
// 获取Token
|
||||||
}
|
String tokenValue = chatContext.getTokenValue();
|
||||||
|
// 获取响应处理器
|
||||||
/**
|
StreamingChatResponseHandler handler = chatContext.getHandler();
|
||||||
* 定义聊天流程骨架
|
|
||||||
*/
|
|
||||||
public SseEmitter executeChat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) {
|
|
||||||
try {
|
try {
|
||||||
String content = Optional.ofNullable(chatRequest.getMessages()).filter(messages -> !messages.isEmpty())
|
String content = Optional.ofNullable(chatRequest.getMessages()).filter(messages -> !messages.isEmpty())
|
||||||
// 对话逻辑:从 messages 筛选第一个元素
|
// 对话逻辑:从 messages 筛选第一个元素
|
||||||
@@ -100,11 +100,11 @@ public abstract class AbstractStreamingChatService implements IChatService {
|
|||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
// 工作流逻辑:从 chatMessages 筛选 UserMessage 的文本
|
// 工作流逻辑:从 chatMessages 筛选 UserMessage 的文本
|
||||||
.orElseGet(() -> Optional.ofNullable(chatRequest.getChatMessages()).orElse(List.of()).stream()
|
.orElseGet(() -> Optional.ofNullable(chatRequest.getChatMessages()).orElse(List.of()).stream()
|
||||||
.filter(message -> message instanceof UserMessage um)
|
.filter(message -> message instanceof UserMessage um)
|
||||||
.map(message -> ((UserMessage) message).singleText())
|
.map(message -> ((UserMessage) message).singleText())
|
||||||
.filter(StringUtils::isNotBlank)
|
.filter(StringUtils::isNotBlank)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(""));
|
.orElse(""));
|
||||||
|
|
||||||
// 保存用户消息
|
// 保存用户消息
|
||||||
saveChatMessage(chatRequest, userId, content, RoleType.USER.getName(), chatModelVo);
|
saveChatMessage(chatRequest, userId, content, RoleType.USER.getName(), chatModelVo);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.ruoyi.common.chat.Service.IChatModelService;
|
|||||||
import org.ruoyi.common.chat.Service.IChatService;
|
import org.ruoyi.common.chat.Service.IChatService;
|
||||||
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
|
||||||
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
|
||||||
|
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
|
||||||
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
|
||||||
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
import org.ruoyi.common.chat.factory.ChatServiceFactory;
|
||||||
import org.ruoyi.common.satoken.utils.LoginHelper;
|
import org.ruoyi.common.satoken.utils.LoginHelper;
|
||||||
@@ -71,7 +72,16 @@ public class ChatServiceFacade {
|
|||||||
Long userId = LoginHelper.getUserId();
|
Long userId = LoginHelper.getUserId();
|
||||||
String tokenValue = StpUtil.getTokenValue();
|
String tokenValue = StpUtil.getTokenValue();
|
||||||
SseEmitter emitter = sseEmitterManager.connect(userId, tokenValue);
|
SseEmitter emitter = sseEmitterManager.connect(userId, tokenValue);
|
||||||
return chatService.chat(chatModelVo, chatRequest,emitter,userId, tokenValue);
|
|
||||||
|
// 5. 创建对话上下文对象
|
||||||
|
ChatContext chatContext = ChatContext.builder()
|
||||||
|
.chatModelVo(chatModelVo)
|
||||||
|
.chatRequest(chatRequest)
|
||||||
|
.emitter(emitter)
|
||||||
|
.userId(userId)
|
||||||
|
.tokenValue(tokenValue)
|
||||||
|
.build();
|
||||||
|
return chatService.chat(chatContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user