zhou zhou
昨天 dad32aa4514963b724bf4c930212e61053fdd958
rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/chat/AiChatFailureHandler.java
@@ -8,6 +8,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.time.Instant;
@@ -22,19 +24,19 @@
    private final AiStreamStateStore aiStreamStateStore;
    public AiChatException buildAiException(String code, AiErrorCategory category, String stage, String message, Throwable cause) {
        return new AiChatException(code, category, stage, message, cause);
        return new AiChatException(code, category, stage, resolveExceptionMessage(message, cause), cause);
    }
    public void handleStreamFailure(SseEmitter emitter, String requestId, Long sessionId, String model, long startedAt,
                                    Long firstTokenAt, AiChatException exception, Long callLogId,
                                    long toolSuccessCount, long toolFailureCount,
                                    AiThinkingTraceEmitter thinkingTraceEmitter,
                                    AiChatTraceEmitter traceEmitter,
                                    Long tenantId, Long userId, String promptCode) {
        if (isClientAbortException(exception)) {
            log.warn("AI chat aborted by client, requestId={}, sessionId={}, stage={}, message={}",
                    requestId, sessionId, exception.getStage(), exception.getMessage());
            if (thinkingTraceEmitter != null) {
                thinkingTraceEmitter.markTerminated("ABORTED");
            if (traceEmitter != null) {
                traceEmitter.markTerminated("ABORTED");
            }
            aiSseEventPublisher.emitSafely(emitter, "status",
                    aiSseEventPublisher.buildTerminalStatus(requestId, sessionId, "ABORTED", model, startedAt, firstTokenAt));
@@ -50,13 +52,13 @@
                    toolFailureCount
            );
            aiStreamStateStore.markStreamState(requestId, tenantId, userId, sessionId, promptCode, "ABORTED", exception.getMessage());
            emitter.completeWithError(exception);
            emitter.complete();
            return;
        }
        log.error("AI chat failed, requestId={}, sessionId={}, category={}, stage={}, message={}",
                requestId, sessionId, exception.getCategory(), exception.getStage(), exception.getMessage(), exception);
        if (thinkingTraceEmitter != null) {
            thinkingTraceEmitter.markTerminated("FAILED");
        if (traceEmitter != null) {
            traceEmitter.markTerminated("FAILED");
        }
        aiSseEventPublisher.emitSafely(emitter, "status",
                aiSseEventPublisher.buildTerminalStatus(requestId, sessionId, "FAILED", model, startedAt, firstTokenAt));
@@ -81,7 +83,36 @@
                toolFailureCount
        );
        aiStreamStateStore.markStreamState(requestId, tenantId, userId, sessionId, promptCode, "FAILED", exception.getMessage());
        emitter.completeWithError(exception);
        emitter.complete();
    }
    private String resolveExceptionMessage(String message, Throwable cause) {
        String upstreamMessage = extractUpstreamResponseBody(cause);
        if (StringUtils.hasText(upstreamMessage)) {
            return truncateMessage(upstreamMessage);
        }
        return truncateMessage(message);
    }
    private String extractUpstreamResponseBody(Throwable throwable) {
        Throwable current = throwable;
        while (current != null) {
            if (current instanceof WebClientResponseException webClientResponseException) {
                String responseBody = webClientResponseException.getResponseBodyAsString();
                if (StringUtils.hasText(responseBody)) {
                    return responseBody.replace('\n', ' ').replace('\r', ' ').trim();
                }
            }
            current = current.getCause();
        }
        return null;
    }
    private String truncateMessage(String message) {
        if (!StringUtils.hasText(message)) {
            return message;
        }
        return message.length() > 900 ? message.substring(0, 900) : message;
    }
    private boolean isClientAbortException(Throwable throwable) {