diff --git a/src/main/java/com/company/agentfoundation/integration/coze/client/CozeWorkflowClient.java b/src/main/java/com/company/agentfoundation/integration/coze/client/CozeWorkflowClient.java index 9f1f786..43ee28a 100644 --- a/src/main/java/com/company/agentfoundation/integration/coze/client/CozeWorkflowClient.java +++ b/src/main/java/com/company/agentfoundation/integration/coze/client/CozeWorkflowClient.java @@ -2,7 +2,6 @@ package com.company.agentfoundation.integration.coze.client; import com.company.agentfoundation.config.FoundationProperties; import com.company.agentfoundation.integration.coze.dto.CozeWorkflowRequest; -import com.fasterxml.jackson.databind.JsonNode; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -23,13 +22,13 @@ public class CozeWorkflowClient { .build(); } - public JsonNode chat(CozeWorkflowRequest request) { + public String chat(CozeWorkflowRequest request) { return webClient.post() .uri(properties.getCoze().getChatPath()) .header(HttpHeaders.AUTHORIZATION, "Bearer " + properties.getCoze().getApiKey()) .bodyValue(request) .retrieve() - .bodyToMono(JsonNode.class) + .bodyToMono(String.class) .retryWhen(Retry.backoff(properties.getCoze().getMaxRetries(), Duration.ofMillis(300))) .block(Duration.ofMillis(properties.getCoze().getReadTimeoutMs())); } diff --git a/src/main/java/com/company/agentfoundation/integration/coze/service/CozeWorkflowService.java b/src/main/java/com/company/agentfoundation/integration/coze/service/CozeWorkflowService.java index 162f05c..2aae5ef 100644 --- a/src/main/java/com/company/agentfoundation/integration/coze/service/CozeWorkflowService.java +++ b/src/main/java/com/company/agentfoundation/integration/coze/service/CozeWorkflowService.java @@ -55,7 +55,7 @@ public class CozeWorkflowService { List.of(new CozeMessage("text", "user", "question", userText == null ? "" : userText)) ); - JsonNode response = client.chat(request); + String response = client.chat(request); String reply = extractReply(response).orElse(""); log.info("coze workflow chat success workflowKey={} replyPreview={}", workflowKey, preview(reply)); return reply; @@ -117,6 +117,76 @@ public class CozeWorkflowService { return Optional.empty(); } + private Optional extractReply(String response) { + if (isBlank(response)) { + return Optional.empty(); + } + String trimmed = response.trim(); + if (isJson(trimmed)) { + try { + return extractReply(objectMapper.readTree(trimmed)); + } catch (Exception e) { + log.warn("coze workflow json response parse failed err={}", e.getMessage()); + } + } + Optional eventReply = extractFromEventStream(trimmed); + if (eventReply.isPresent()) { + return eventReply; + } + if (looksLikeEventStream(trimmed)) { + log.warn("coze workflow event stream response did not include completed content"); + return Optional.empty(); + } + return Optional.of(trimmed); + } + + private Optional extractFromEventStream(String response) { + String currentEvent = ""; + StringBuilder data = new StringBuilder(); + String[] lines = response.replace("\r\n", "\n").replace('\r', '\n').split("\n", -1); + for (String line : lines) { + if (line.isBlank()) { + Optional content = extractCompletedEventContent(currentEvent, data.toString()); + if (content.isPresent()) { + return content; + } + currentEvent = ""; + data.setLength(0); + continue; + } + if (line.startsWith("event:")) { + currentEvent = line.substring("event:".length()).trim(); + } else if (line.startsWith("data:")) { + if (data.length() > 0) { + data.append('\n'); + } + data.append(line.substring("data:".length()).trim()); + } + } + return extractCompletedEventContent(currentEvent, data.toString()); + } + + private Optional extractCompletedEventContent(String event, String data) { + if (!"conversation.message.completed".equals(event) || isBlank(data) || !isJson(data.trim())) { + return Optional.empty(); + } + try { + JsonNode message = objectMapper.readTree(data); + String role = message.path("role").asText(""); + String type = message.path("type").asText(""); + if (("assistant".equalsIgnoreCase(role) || role.isBlank()) && ("answer".equalsIgnoreCase(type) || type.isBlank())) { + return firstText(message, "content", "text", "answer", "output"); + } + } catch (Exception e) { + log.warn("coze workflow completed event parse failed err={}", e.getMessage()); + } + return Optional.empty(); + } + + private boolean looksLikeEventStream(String response) { + return response.startsWith("event:") || response.contains("\nevent:") || response.startsWith("data:") || response.contains("\ndata:"); + } + private Optional extractFromMessages(JsonNode messages) { if (messages == null || !messages.isArray()) { return Optional.empty(); diff --git a/src/main/java/com/company/agentfoundation/integration/wecom/controller/WecomCallbackController.java b/src/main/java/com/company/agentfoundation/integration/wecom/controller/WecomCallbackController.java index 8ab4641..bfe6606 100644 --- a/src/main/java/com/company/agentfoundation/integration/wecom/controller/WecomCallbackController.java +++ b/src/main/java/com/company/agentfoundation/integration/wecom/controller/WecomCallbackController.java @@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; public class WecomCallbackController { private static final Logger log = LoggerFactory.getLogger(WecomCallbackController.class); private static final long MESSAGE_DEDUP_TTL_SECONDS = 600; - private static final String FIXED_REPLY = "\u4f60\u597d"; + private static final String ERROR_REPLY = "\u6263\u5b50\u667a\u80fd\u4f53\u8c03\u7528\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5"; private final CozeWorkflowService cozeWorkflowService; private final WecomClient wecomClient; private final WecomCryptoService wecomCryptoService; @@ -97,11 +97,11 @@ public class WecomCallbackController { reply = cozeWorkflowService.chatDefault(userText); } catch (Exception e) { log.error("wecom callback coze workflow failed fromUser={} err={}", msg.getFromUserName(), e.getMessage(), e); - reply = FIXED_REPLY; + reply = ERROR_REPLY; } if (reply == null || reply.isBlank()) { - log.warn("wecom callback coze reply blank, fallback to fixed reply"); - reply = FIXED_REPLY; + log.warn("wecom callback coze reply blank, fallback to error reply"); + reply = ERROR_REPLY; } log.info("wecom callback sending coze reply toUser={} replyPreview={}", msg.getFromUserName(), preview(reply)); wecomClient.sendText(msg.getFromUserName(), reply);