context:公共聊天接口调整为对话上下文对象传输

This commit is contained in:
zengxb
2026-02-14 10:55:53 +08:00
parent 420e05ecf3
commit b40805cb57
5 changed files with 106 additions and 33 deletions

View File

@@ -1,8 +1,7 @@
package org.ruoyi.common.chat.Service;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import org.ruoyi.common.chat.domain.dto.request.ChatRequest;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import jakarta.validation.Valid;
import org.ruoyi.common.chat.domain.entity.chat.ChatContext;
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(ChatModelVo chatModelVo, ChatRequest chatRequest,SseEmitter emitter,Long userId,String tokenValue, StreamingChatResponseHandler handler);
SseEmitter chat(@Valid ChatContext chatContext);
/**
* 获取服务提供商名称

View File

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

View File

@@ -14,6 +14,7 @@ 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.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.factory.ChatServiceFactory;
import org.ruoyi.workflow.base.NodeInputConfigTypeHandler;
@@ -135,9 +136,11 @@ 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<>();
@@ -151,9 +154,18 @@ public class WorkflowUtil {
chatRequest.setModel(modelName);
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(chatModelVo, chatRequest, sseEmitter, userId, tokenValue, handler);
chatService.chat(chatContext);
wfState.getNodeToStreamingGenerator().put(node.getUuid(), streamingGenerator);
}

View File

@@ -26,6 +26,7 @@ import org.ruoyi.agent.tool.QueryAllTablesTool;
import org.ruoyi.agent.tool.QueryTableSchemaTool;
import org.ruoyi.common.chat.Service.IChatService;
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.core.utils.ObjectUtils;
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.impl.memory.PersistentChatMemoryStore;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.*;
@@ -55,6 +57,7 @@ import java.util.concurrent.ConcurrentHashMap;
* @date 2025/12/13
*/
@Slf4j
@Validated
public abstract class AbstractStreamingChatService implements IChatService {
/**
@@ -77,22 +80,19 @@ public abstract class AbstractStreamingChatService implements IChatService {
* 定义聊天流程骨架
*/
@Override
public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue) {
return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, null);
}
/**
* 定义聊天流程骨架(包含流式回调结构)
*/
@Override
public SseEmitter chat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) {
return executeChat(chatModelVo, chatRequest, emitter, userId, tokenValue, handler);
}
/**
* 定义聊天流程骨架
*/
public SseEmitter executeChat(ChatModelVo chatModelVo, ChatRequest chatRequest, SseEmitter emitter, Long userId, String tokenValue, StreamingChatResponseHandler handler) {
public SseEmitter chat(ChatContext chatContext) {
// 获取模型管理视图对象
ChatModelVo chatModelVo = chatContext.getChatModelVo();
// 获取对话请求对象
ChatRequest chatRequest = chatContext.getChatRequest();
// 获取SSe连接对象
SseEmitter emitter = chatContext.getEmitter();
// 获取用户ID
Long userId = chatContext.getUserId();
// 获取Token
String tokenValue = chatContext.getTokenValue();
// 获取响应处理器
StreamingChatResponseHandler handler = chatContext.getHandler();
try {
String content = Optional.ofNullable(chatRequest.getMessages()).filter(messages -> !messages.isEmpty())
// 对话逻辑:从 messages 筛选第一个元素
@@ -100,11 +100,11 @@ public abstract class AbstractStreamingChatService implements IChatService {
.filter(StringUtils::isNotBlank)
// 工作流逻辑:从 chatMessages 筛选 UserMessage 的文本
.orElseGet(() -> Optional.ofNullable(chatRequest.getChatMessages()).orElse(List.of()).stream()
.filter(message -> message instanceof UserMessage um)
.map(message -> ((UserMessage) message).singleText())
.filter(StringUtils::isNotBlank)
.findFirst()
.orElse(""));
.filter(message -> message instanceof UserMessage um)
.map(message -> ((UserMessage) message).singleText())
.filter(StringUtils::isNotBlank)
.findFirst()
.orElse(""));
// 保存用户消息
saveChatMessage(chatRequest, userId, content, RoleType.USER.getName(), chatModelVo);

View File

@@ -8,6 +8,7 @@ import org.ruoyi.common.chat.Service.IChatModelService;
import org.ruoyi.common.chat.Service.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.domain.vo.chat.ChatModelVo;
import org.ruoyi.common.chat.factory.ChatServiceFactory;
import org.ruoyi.common.satoken.utils.LoginHelper;
@@ -71,7 +72,16 @@ public class ChatServiceFacade {
Long userId = LoginHelper.getUserId();
String tokenValue = StpUtil.getTokenValue();
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);
}
/**