From 51877df13075ad10ef51107f15bcd21f1661febe Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期二, 17 三月 2026 09:48:01 +0800
Subject: [PATCH] #AI
---
rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java | 217 ++++++++++++++++++++++--------------------------------
1 files changed, 89 insertions(+), 128 deletions(-)
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java
index 3768be3..49322ec 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/ai/controller/AiController.java
@@ -2,27 +2,32 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.vincent.rsf.framework.common.R;
-import com.vincent.rsf.server.ai.config.AiProperties;
+import com.vincent.rsf.server.ai.constant.AiSceneCode;
import com.vincent.rsf.server.ai.dto.AiChatStreamRequest;
import com.vincent.rsf.server.ai.dto.AiSessionCreateRequest;
import com.vincent.rsf.server.ai.dto.AiSessionRenameRequest;
-import com.vincent.rsf.server.ai.dto.GatewayChatMessage;
-import com.vincent.rsf.server.ai.dto.GatewayChatRequest;
import com.vincent.rsf.server.ai.model.AiChatMessage;
import com.vincent.rsf.server.ai.model.AiChatSession;
import com.vincent.rsf.server.ai.model.AiPromptContext;
-import com.vincent.rsf.server.ai.service.AiGatewayClient;
-import com.vincent.rsf.server.ai.service.AiPromptContextService;
+import com.vincent.rsf.server.ai.service.diagnosis.AiChatStreamOrchestrator;
+import com.vincent.rsf.server.ai.service.diagnosis.AiDiagnosisRuntimeService;
+import com.vincent.rsf.server.ai.service.AiModelRouteRuntimeService;
import com.vincent.rsf.server.ai.service.AiRuntimeConfigService;
import com.vincent.rsf.server.ai.service.AiSessionService;
import com.vincent.rsf.server.system.controller.BaseController;
+import com.vincent.rsf.server.system.entity.AiDiagnosisRecord;
import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.annotation.Resource;
import java.io.IOException;
-import java.util.Date;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -34,17 +39,17 @@
@Resource
private AiSessionService aiSessionService;
@Resource
- private AiProperties aiProperties;
- @Resource
- private AiGatewayClient aiGatewayClient;
- @Resource
private AiRuntimeConfigService aiRuntimeConfigService;
@Resource
- private AiPromptContextService aiPromptContextService;
+ private AiModelRouteRuntimeService aiModelRouteRuntimeService;
+ @Resource
+ private AiDiagnosisRuntimeService aiDiagnosisRuntimeService;
+ @Resource
+ private AiChatStreamOrchestrator aiChatStreamOrchestrator;
@GetMapping("/model/list")
public R modelList() {
- List<Map<String, Object>> models = new java.util.ArrayList<>();
+ List<Map<String, Object>> models = new ArrayList<>();
for (AiRuntimeConfigService.ModelRuntimeConfig model : aiRuntimeConfigService.listEnabledModels()) {
Map<String, Object> item = new LinkedHashMap<>();
item.put("code", model.getCode());
@@ -99,6 +104,20 @@
@PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter chatStream(@RequestBody AiChatStreamRequest request) {
+ return doChatStream(normalizeRequest(request));
+ }
+
+ @PostMapping(value = "/diagnose/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+ public SseEmitter diagnoseStream(@RequestBody(required = false) AiChatStreamRequest request) {
+ AiChatStreamRequest diagnosisRequest = normalizeRequest(request);
+ diagnosisRequest.setSceneCode(AiSceneCode.SYSTEM_DIAGNOSE);
+ if (diagnosisRequest.getMessage() == null || diagnosisRequest.getMessage().trim().isEmpty()) {
+ diagnosisRequest.setMessage("璇峰褰撳墠WMS绯荤粺杩涜涓�娆″贰妫�璇婃柇锛岀粨鍚堝簱瀛樸�佷换鍔°�佽澶囩珯鐐规暟鎹瘑鍒紓甯稿苟缁欏嚭澶勭悊寤鸿銆�");
+ }
+ return doChatStream(diagnosisRequest);
+ }
+
+ private SseEmitter doChatStream(AiChatStreamRequest request) {
SseEmitter emitter = new SseEmitter(0L);
Long tenantId = getTenantId();
Long userId = getLoginUserId();
@@ -110,137 +129,79 @@
completeWithError(emitter, "娑堟伅鍐呭涓嶈兘涓虹┖");
return emitter;
}
+
AiChatSession session = aiSessionService.ensureSession(tenantId, userId, request.getSessionId(), request.getModelCode());
aiSessionService.clearStopFlag(session.getId());
aiSessionService.appendMessage(tenantId, userId, session.getId(), "user", request.getMessage(), session.getModelCode());
- AiRuntimeConfigService.ModelRuntimeConfig modelRuntimeConfig = aiRuntimeConfigService.resolveModel(session.getModelCode());
+
+ AiPromptContext promptContext = new AiPromptContext()
+ .setTenantId(tenantId)
+ .setUserId(userId)
+ .setSessionId(session.getId())
+ .setModelCode(session.getModelCode())
+ .setQuestion(request.getMessage())
+ .setSceneCode(request.getSceneCode());
+
+ List<AiModelRouteRuntimeService.RouteCandidate> candidates = aiModelRouteRuntimeService.resolveCandidates(
+ tenantId,
+ request.getSceneCode(),
+ session.getModelCode()
+ );
+ if (candidates.isEmpty()) {
+ completeWithError(emitter, "鏈壘鍒板彲鐢ㄧ殑AI妯″瀷閰嶇疆");
+ return emitter;
+ }
+ int maxContextMessages = resolveContextSize(candidates);
List<AiChatMessage> contextMessages = aiSessionService.listContextMessages(
tenantId,
userId,
session.getId(),
- modelRuntimeConfig.getMaxContextMessages()
+ maxContextMessages
);
+ AiDiagnosisRecord diagnosisRecord = AiSceneCode.SYSTEM_DIAGNOSE.equals(request.getSceneCode())
+ ? aiDiagnosisRuntimeService.startDiagnosis(tenantId, userId, session.getId(), request.getSceneCode(), request.getMessage())
+ : null;
- Thread thread = new Thread(() -> {
- StringBuilder assistantReply = new StringBuilder();
- boolean doneSent = false;
- try {
- emitter.send(SseEmitter.event().name("session").data(buildSessionPayload(session), MediaType.APPLICATION_JSON));
- GatewayChatRequest gatewayChatRequest = buildGatewayRequest(
- tenantId,
- userId,
- session,
- contextMessages,
- modelRuntimeConfig,
- request.getMessage()
- );
- aiGatewayClient.stream(gatewayChatRequest, event -> handleGatewayEvent(
- emitter,
- event,
- session,
- assistantReply
- ));
- if (aiSessionService.isStopRequested(session.getId())) {
- if (assistantReply.length() > 0) {
- aiSessionService.appendMessage(tenantId, userId, session.getId(), "assistant", assistantReply.toString(), session.getModelCode());
- }
- emitter.send(SseEmitter.event().name("done").data(buildDonePayload(session, true), MediaType.APPLICATION_JSON));
- doneSent = true;
- }
- } catch (Exception e) {
- try {
- emitter.send(SseEmitter.event().name("error").data(buildErrorPayload(e.getMessage()), MediaType.APPLICATION_JSON));
- } catch (IOException ignore) {
- }
- } finally {
- if (!doneSent && assistantReply.length() > 0) {
- aiSessionService.appendMessage(tenantId, userId, session.getId(), "assistant", assistantReply.toString(), session.getModelCode());
- try {
- emitter.send(SseEmitter.event().name("done").data(buildDonePayload(session, false), MediaType.APPLICATION_JSON));
- } catch (IOException ignore) {
- }
- }
- emitter.complete();
- aiSessionService.clearStopFlag(session.getId());
- }
- }, "ai-chat-stream-" + session.getId());
+ Thread thread = new Thread(() -> executeStream(
+ emitter, tenantId, userId, session, request, promptContext, contextMessages, diagnosisRecord, candidates
+ ), "ai-chat-stream-" + session.getId());
thread.setDaemon(true);
thread.start();
return emitter;
}
- private boolean handleGatewayEvent(SseEmitter emitter, JsonNode event, AiChatSession session,
- StringBuilder assistantReply) throws Exception {
- if (aiSessionService.isStopRequested(session.getId())) {
- return false;
- }
- String type = event.path("type").asText();
- if ("delta".equals(type)) {
- String content = event.path("content").asText("");
- assistantReply.append(content);
- emitter.send(SseEmitter.event().name("delta").data(buildDeltaPayload(session, content), MediaType.APPLICATION_JSON));
- return true;
- }
- if ("error".equals(type)) {
- emitter.send(SseEmitter.event().name("error").data(buildErrorPayload(event.path("message").asText("妯″瀷璋冪敤澶辫触")), MediaType.APPLICATION_JSON));
- return false;
- }
- if ("done".equals(type)) {
- return false;
- }
- return true;
+ private void executeStream(SseEmitter emitter,
+ Long tenantId,
+ Long userId,
+ AiChatSession session,
+ AiChatStreamRequest request,
+ AiPromptContext promptContext,
+ List<AiChatMessage> contextMessages,
+ AiDiagnosisRecord diagnosisRecord,
+ List<AiModelRouteRuntimeService.RouteCandidate> candidates) {
+ aiChatStreamOrchestrator.executeStream(
+ emitter,
+ tenantId,
+ userId,
+ session,
+ request,
+ promptContext,
+ contextMessages,
+ diagnosisRecord,
+ candidates
+ );
}
- private GatewayChatRequest buildGatewayRequest(Long tenantId, Long userId, AiChatSession session, List<AiChatMessage> contextMessages,
- AiRuntimeConfigService.ModelRuntimeConfig modelRuntimeConfig,
- String latestQuestion) {
- GatewayChatRequest request = new GatewayChatRequest();
- request.setSessionId(session.getId());
- request.setModelCode(session.getModelCode());
- request.setSystemPrompt(aiPromptContextService.buildSystemPrompt(
- modelRuntimeConfig.getSystemPrompt(),
- new AiPromptContext()
- .setTenantId(tenantId)
- .setUserId(userId)
- .setSessionId(session.getId())
- .setModelCode(session.getModelCode())
- .setQuestion(latestQuestion)
- ));
- request.setChatUrl(modelRuntimeConfig.getChatUrl());
- request.setApiKey(modelRuntimeConfig.getApiKey());
- request.setModelName(modelRuntimeConfig.getModelName());
- for (AiChatMessage contextMessage : contextMessages) {
- GatewayChatMessage item = new GatewayChatMessage();
- item.setRole(contextMessage.getRole());
- item.setContent(contextMessage.getContent());
- request.getMessages().add(item);
+ private int resolveContextSize(List<AiModelRouteRuntimeService.RouteCandidate> candidates) {
+ return aiChatStreamOrchestrator.resolveContextSize(candidates);
+ }
+
+ private AiChatStreamRequest normalizeRequest(AiChatStreamRequest request) {
+ AiChatStreamRequest normalized = request == null ? new AiChatStreamRequest() : request;
+ if (normalized.getSceneCode() == null || normalized.getSceneCode().trim().isEmpty()) {
+ normalized.setSceneCode(AiSceneCode.GENERAL_CHAT);
}
- return request;
- }
-
- private Map<String, Object> buildSessionPayload(AiChatSession session) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("sessionId", session.getId());
- payload.put("title", session.getTitle());
- payload.put("modelCode", session.getModelCode());
- return payload;
- }
-
- private Map<String, Object> buildDeltaPayload(AiChatSession session, String content) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("sessionId", session.getId());
- payload.put("modelCode", session.getModelCode());
- payload.put("content", content);
- payload.put("timestamp", new Date().getTime());
- return payload;
- }
-
- private Map<String, Object> buildDonePayload(AiChatSession session, boolean stopped) {
- Map<String, Object> payload = new LinkedHashMap<>();
- payload.put("sessionId", session.getId());
- payload.put("modelCode", session.getModelCode());
- payload.put("stopped", stopped);
- return payload;
+ return normalized;
}
private Map<String, Object> buildErrorPayload(String message) {
@@ -256,5 +217,5 @@
}
emitter.complete();
}
-
}
+
--
Gitblit v1.9.1