diff --git a/backend-single/src/main/java/com/emotion/service/impl/AiChatServiceImpl.java b/backend-single/src/main/java/com/emotion/service/impl/AiChatServiceImpl.java index f824897..c2cd444 100644 --- a/backend-single/src/main/java/com/emotion/service/impl/AiChatServiceImpl.java +++ b/backend-single/src/main/java/com/emotion/service/impl/AiChatServiceImpl.java @@ -13,6 +13,8 @@ import com.emotion.service.ConversationService; import com.emotion.service.CozeApiCallService; import com.emotion.service.EmotionRecordService; import com.emotion.service.EmotionAnalysisService; +import com.emotion.service.AiConfigService; +import com.emotion.entity.AiConfig; import com.emotion.dto.request.*; import com.emotion.dto.response.*; import com.emotion.util.SnowflakeIdGenerator; @@ -41,6 +43,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; import java.util.Arrays; +import java.util.stream.Collectors; /** * AI聊天服务实现类 @@ -73,37 +76,15 @@ public class AiChatServiceImpl implements AiChatService { @Autowired private SnowflakeIdGenerator snowflakeIdGenerator; - @Value("${emotion.coze.api.token:}") - private String cozeApiToken; - - @Value("${emotion.coze.api.base-url:https://api.coze.cn}") - private String cozeBaseUrl; - - @Value("${emotion.coze.api.chat.path:/v3/chat}") - private String chatPath; - - @Value("${emotion.coze.api.chat.talk.bot-id:}") - private String chatBotId; - - @Value("${emotion.coze.api.chat.talk.workflow-id:}") - private String chatWorkflowId; - - @Value("${emotion.coze.api.chat.summary.bot-id:}") - private String summaryBotId; - - @Value("${emotion.coze.api.chat.summary.workflow-id:}") - private String summaryWorkflowId; - - @Value("${emotion.coze.api.timeout:30000}") - private int timeout; - - @Value("${emotion.coze.api.retry-count:3}") - private int retryCount; - - @Value("${emotion.coze.api.retry-delay:1000}") - private int retryDelay; + @Autowired + private AiConfigService aiConfigService; private static final String DEFAULT_USER_ID = "emotion-museum-user"; + + // 使用场景常量 + private static final String USAGE_SCENARIO_CHAT = "chat"; + private static final String USAGE_SCENARIO_SUMMARY = "summary"; + private static final String DEFAULT_ENVIRONMENT = "production"; // API 相关常量 private static final String CONTENT_KEY = "content"; @@ -494,13 +475,20 @@ public class AiChatServiceImpl implements AiChatService { @Override public boolean healthCheck() { try { - // 简化健康检查 - 检查必要配置是否存在 - boolean configValid = cozeApiToken != null && !cozeApiToken.trim().isEmpty() && - chatBotId != null && !chatBotId.trim().isEmpty() && - cozeBaseUrl != null && !cozeBaseUrl.trim().isEmpty(); + // 检查聊天场景的AI配置是否存在 + AiConfig chatConfig = aiConfigService.getBestConfig(USAGE_SCENARIO_CHAT, DEFAULT_ENVIRONMENT); + if (chatConfig == null) { + log.warn("未找到聊天场景的AI配置"); + return false; + } + + // 检查必要配置是否完整 + boolean configValid = chatConfig.getApiToken() != null && !chatConfig.getApiToken().trim().isEmpty() && + chatConfig.getBotId() != null && !chatConfig.getBotId().trim().isEmpty() && + chatConfig.getApiBaseUrl() != null && !chatConfig.getApiBaseUrl().trim().isEmpty(); if (!configValid) { - log.warn("Coze API 配置不完整"); + log.warn("AI配置不完整: configId={}", chatConfig.getId()); return false; } @@ -587,65 +575,89 @@ public class AiChatServiceImpl implements AiChatService { CozeApiCall apiCall = createSummaryApiCallRecord(conversationId, null, userMessage, userId, "summary"); try { - // 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + cozeApiToken); - headers.set("Content-Type", "application/json"); - - // 构建请求体 - 使用总结专用的bot和workflow - Map requestBody = buildSummaryRequest(conversationId, userMessage, userId); - - // 更新API调用记录的请求信息 - updateApiCallRequest(apiCall, cozeBaseUrl + chatPath, requestBody, headers); - - HttpEntity> request = new HttpEntity<>(requestBody, headers); - - // 构建完整的API URL - String cozeApiUrl = cozeBaseUrl + chatPath; - log.info("发送Coze总结请求到: {}, 请求体: {}", cozeApiUrl, requestBody); - - // 发送请求 - ResponseEntity response = restTemplate.exchange( - cozeApiUrl, - HttpMethod.POST, - request, - String.class); - - log.info("收到Coze总结初始响应: {}", response.getBody()); - - // 更新API调用记录的响应信息 - updateApiCallResponse(apiCall, response); - - // 解析响应获取chat_id和conversation_id - JSONObject responseJson = JSON.parseObject(response.getBody()); - String chatId = extractChatIdFromResponse(responseJson); - String cozeConversationId = extractConversationIdFromResponse(responseJson); - - if (chatId != null && cozeConversationId != null) { - // 更新API调用记录的Coze ID信息 - updateApiCallCozeIds(apiCall, chatId, cozeConversationId); - - // 轮询聊天状态直到完成并获取回复内容 - String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall); - log.info("Coze AI总结响应成功: reply={}", aiReply); - - // 更新API调用记录的最终结果 - updateApiCallSuccess(apiCall, aiReply); - - return aiReply; - } else { - log.error("无法从Coze总结响应中获取chat_id或conversation_id"); - updateApiCallError(apiCall, "INVALID_RESPONSE", "无法从Coze总结响应中获取chat_id或conversation_id"); - return "抱歉,AI总结服务响应异常,请稍后再试。"; - } - + return executeSummaryCozeApiCall(apiCall, conversationId, userMessage, userId); } catch (Exception e) { - log.error("发送总结消息到Coze AI失败", e); - updateApiCallError(apiCall, "REQUEST_FAILED", e.getMessage()); + log.error("发送总结消息失败", e); + updateApiCallFailure(apiCall, e.getMessage()); return "抱歉,AI总结服务暂时不可用,请稍后再试。"; } } + /** + * 执行总结Coze API调用的逻辑 + */ + private String executeSummaryCozeApiCall(CozeApiCall apiCall, String conversationId, String userMessage, String userId) { + // 获取总结场景的AI配置 + AiConfig config = getSummaryAiConfig(); + + // 构建请求体 - 使用总结专用的bot和workflow + Map requestBody = buildSummaryRequest(conversationId, userMessage, userId); + + // 检查是否使用流式输出 + boolean useStream = (Boolean) requestBody.get("stream"); + + if (useStream) { + return executeStreamCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId); + } else { + return executeSummaryNormalCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId); + } + } + + /** + * 执行普通(非流式)总结Coze API调用 + */ + private String executeSummaryNormalCozeApiCall(CozeApiCall apiCall, AiConfig config, Map requestBody, + String conversationId, String userMessage, String userId) { + // 构建请求头 + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + config.getApiToken()); + headers.set("Content-Type", "application/json"); + + // 构建完整的API URL + String cozeApiUrl = config.getApiBaseUrl() + getApiPath(config); + + // 更新API调用记录的请求信息 + updateApiCallRequest(apiCall, cozeApiUrl, requestBody, headers); + + HttpEntity> request = new HttpEntity<>(requestBody, headers); + log.info("发送Coze总结请求到: {}, 请求体: {}", cozeApiUrl, requestBody); + + // 发送请求 + ResponseEntity response = restTemplate.exchange( + cozeApiUrl, + HttpMethod.POST, + request, + String.class); + + log.info("收到Coze总结初始响应: {}", response.getBody()); + + // 更新API调用记录的响应信息 + updateApiCallResponse(apiCall, response); + + // 解析响应获取chat_id和conversation_id + JSONObject responseJson = JSON.parseObject(response.getBody()); + String chatId = extractChatIdFromResponse(responseJson); + String cozeConversationId = extractConversationIdFromResponse(responseJson); + + if (chatId != null && cozeConversationId != null) { + // 更新API调用记录的Coze ID信息 + updateApiCallCozeIds(apiCall, chatId, cozeConversationId); + + // 轮询聊天状态直到完成并获取回复内容 + String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall); + log.info("Coze AI总结响应成功: reply={}", aiReply); + + // 更新API调用记录的最终结果 + updateApiCallSuccess(apiCall, aiReply); + + return aiReply; + } else { + log.error("无法从Coze总结响应中获取chat_id或conversation_id"); + updateApiCallError(apiCall, "INVALID_RESPONSE", "无法从Coze总结响应中获取chat_id或conversation_id"); + return "抱歉,AI总结服务响应异常,请稍后再试。"; + } + } + @Override public EmotionSummaryGenerateResponse generateEmotionSummaryWithResponse(String userId) { log.info("生成用户情绪记录总结: userId={}", userId); @@ -719,9 +731,11 @@ public class AiChatServiceImpl implements AiChatService { public boolean isServiceAvailable() { try { - // 简单的健康检查 - return cozeApiToken != null && !cozeApiToken.isEmpty() && - chatBotId != null && !chatBotId.isEmpty(); + // 检查聊天场景的AI配置是否可用 + AiConfig chatConfig = aiConfigService.getBestConfig(USAGE_SCENARIO_CHAT, DEFAULT_ENVIRONMENT); + return chatConfig != null && + chatConfig.getApiToken() != null && !chatConfig.getApiToken().isEmpty() && + chatConfig.getBotId() != null && !chatConfig.getBotId().isEmpty(); } catch (Exception e) { log.error("检查AI服务可用性失败", e); return false; @@ -751,22 +765,40 @@ public class AiChatServiceImpl implements AiChatService { * 执行Coze API调用的公共逻辑 */ private String executeCozeApiCall(CozeApiCall apiCall, String conversationId, String userMessage, String userId) { + // 获取聊天场景的AI配置 + AiConfig config = getChatAiConfig(); + + // 构建请求体 - 使用正确的Coze API格式 + Map requestBody = buildCozeRequestWithConfig(conversationId, userMessage, userId, config); + + // 检查是否使用流式输出 + boolean useStream = (Boolean) requestBody.get("stream"); + + if (useStream) { + return executeStreamCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId); + } else { + return executeNormalCozeApiCall(apiCall, config, requestBody, conversationId, userMessage, userId); + } + } + + /** + * 执行普通(非流式)Coze API调用 + */ + private String executeNormalCozeApiCall(CozeApiCall apiCall, AiConfig config, Map requestBody, + String conversationId, String userMessage, String userId) { // 构建请求头 HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + cozeApiToken); + headers.set("Authorization", "Bearer " + config.getApiToken()); headers.set("Content-Type", "application/json"); - // 构建请求体 - 使用正确的Coze API格式 - Map requestBody = buildCozeRequest(conversationId, userMessage, userId); - + // 构建完整的API URL + String cozeApiUrl = config.getApiBaseUrl() + getApiPath(config); + // 更新API调用记录的请求信息 - updateApiCallRequest(apiCall, cozeBaseUrl + chatPath, requestBody, headers); + updateApiCallRequest(apiCall, cozeApiUrl, requestBody, headers); HttpEntity> request = new HttpEntity<>(requestBody, headers); - - // 构建完整的API URL - String cozeApiUrl = cozeBaseUrl + chatPath; - log.info("发送Coze请求到: {}, 请求体: {}", cozeApiUrl, requestBody); + log.info("发送Coze普通请求到: {}, 请求体: {}", cozeApiUrl, requestBody); // 发送请求 ResponseEntity response = restTemplate.exchange( @@ -791,7 +823,7 @@ public class AiChatServiceImpl implements AiChatService { // 轮询聊天状态直到完成并获取回复内容 String aiReply = waitForChatCompletionWithTracking(chatId, cozeConversationId, apiCall); - log.info("Coze AI响应成功: reply={}", aiReply); + log.info("Coze AI普通响应成功: reply={}", aiReply); // 更新API调用记录的最终结果 updateApiCallSuccess(apiCall, aiReply); @@ -804,6 +836,165 @@ public class AiChatServiceImpl implements AiChatService { } } + /** + * 执行流式Coze API调用 + */ + private String executeStreamCozeApiCall(CozeApiCall apiCall, AiConfig config, Map requestBody, + String conversationId, String userMessage, String userId) { + try { + // 构建请求头 + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + config.getApiToken()); + headers.set("Content-Type", "application/json"); + headers.set("Accept", "text/event-stream"); + + // 构建完整的API URL + String cozeApiUrl = config.getApiBaseUrl() + getApiPath(config); + + // 更新API调用记录的请求信息 + updateApiCallRequest(apiCall, cozeApiUrl, requestBody, headers); + + log.info("发送Coze流式请求到: {}, 请求体: {}", cozeApiUrl, requestBody); + + // 使用RestTemplate处理流式响应 + String streamResponse = handleStreamResponse(cozeApiUrl, headers, requestBody, apiCall); + + log.info("Coze AI流式响应完成: length={}", streamResponse != null ? streamResponse.length() : 0); + + // 更新API调用记录的最终结果 + updateApiCallSuccess(apiCall, streamResponse); + + return streamResponse; + + } catch (Exception e) { + log.error("流式API调用失败", e); + updateApiCallError(apiCall, "STREAM_ERROR", e.getMessage()); + return "抱歉,AI流式服务暂时不可用,请稍后再试。"; + } + } + + /** + * 处理流式响应 + */ + private String handleStreamResponse(String url, HttpHeaders headers, Map requestBody, CozeApiCall apiCall) { + try { + // 创建HTTP客户端 + java.net.http.HttpClient client = java.net.http.HttpClient.newBuilder() + .connectTimeout(java.time.Duration.ofSeconds(30)) + .build(); + + // 构建请求 + java.net.http.HttpRequest.Builder requestBuilder = java.net.http.HttpRequest.newBuilder() + .uri(java.net.URI.create(url)) + .timeout(java.time.Duration.ofMinutes(5)) + .POST(java.net.http.HttpRequest.BodyPublishers.ofString(JSON.toJSONString(requestBody))); + + // 添加请求头 + headers.forEach((key, values) -> { + if (values != null && !values.isEmpty()) { + requestBuilder.header(key, values.get(0)); + } + }); + + java.net.http.HttpRequest request = requestBuilder.build(); + + // 发送请求并处理流式响应 + StringBuilder responseBuilder = new StringBuilder(); + StringBuilder fullStreamData = new StringBuilder(); + + java.net.http.HttpResponse> response = client.send(request, + java.net.http.HttpResponse.BodyHandlers.ofLines()); + + log.info("流式响应状态码: {}", response.statusCode()); + + if (response.statusCode() != 200) { + String errorBody = response.body().collect(java.util.stream.Collectors.joining("\n")); + log.error("流式请求失败,状态码: {}, 响应: {}", response.statusCode(), errorBody); + return "流式请求失败,状态码: " + response.statusCode(); + } + + // 处理流式数据 + response.body().forEach(line -> { + fullStreamData.append(line).append("\n"); + + if (line.startsWith("data: ")) { + String data = line.substring(6).trim(); + + if ("[DONE]".equals(data)) { + log.info("流式响应完成"); + return; + } + + try { + JSONObject jsonData = JSON.parseObject(data); + + // 提取消息内容 + if (jsonData.containsKey("choices")) { + com.alibaba.fastjson2.JSONArray choices = jsonData.getJSONArray("choices"); + if (choices != null && !choices.isEmpty()) { + JSONObject choice = choices.getJSONObject(0); + if (choice != null && choice.containsKey("delta")) { + JSONObject delta = choice.getJSONObject("delta"); + if (delta != null && delta.containsKey("content")) { + String content = delta.getString("content"); + if (content != null) { + responseBuilder.append(content); + } + } + } + } + } + + // Coze特定格式处理 + if (jsonData.containsKey("event")) { + String event = jsonData.getString("event"); + if ("conversation.message.delta".equals(event) && jsonData.containsKey("data")) { + JSONObject eventData = jsonData.getJSONObject("data"); + if (eventData != null && eventData.containsKey("content")) { + String content = eventData.getString("content"); + if (content != null) { + responseBuilder.append(content); + } + } + } + } + + } catch (Exception e) { + log.warn("解析流式数据失败: {}, 数据: {}", e.getMessage(), data); + } + } + }); + + // 记录完整的流式数据用于调试 + updateApiCallStreamData(apiCall, fullStreamData.toString()); + + String finalResponse = responseBuilder.toString(); + if (finalResponse.isEmpty()) { + log.warn("流式响应为空,返回默认消息"); + return "收到了流式响应,但内容为空。"; + } + + return finalResponse; + + } catch (Exception e) { + log.error("处理流式响应失败", e); + throw new RuntimeException("处理流式响应失败: " + e.getMessage(), e); + } + } + + /** + * 更新API调用记录的流式数据 + */ + private void updateApiCallStreamData(CozeApiCall apiCall, String streamData) { + try { + // 可以将流式数据存储到响应体字段中,用于调试和分析 + apiCall.setResponseBody(streamData); + cozeApiCallService.updateById(apiCall); + } catch (Exception e) { + log.error("更新API调用记录流式数据失败: {}", e.getMessage(), e); + } + } + public Map guestChat(String message, String clientIp) { log.info("访客聊天: message={}, clientIp={}", message, clientIp); @@ -883,16 +1074,28 @@ public class AiChatServiceImpl implements AiChatService { * 构建Coze API请求 - 根据官方文档修正格式 */ private Map buildCozeRequest(String conversationId, String userMessage, String userId) { + // 获取聊天场景的AI配置 + AiConfig config = getChatAiConfig(); + return buildCozeRequestWithConfig(conversationId, userMessage, userId, config); + } + + /** + * 使用指定配置构建Coze API请求 + */ + private Map buildCozeRequestWithConfig(String conversationId, String userMessage, String userId, AiConfig config) { Map cozeRequest = new HashMap<>(); - cozeRequest.put("bot_id", chatBotId); + cozeRequest.put("bot_id", config.getBotId()); // 如果有workflow_id,则添加 - if (chatWorkflowId != null && !chatWorkflowId.trim().isEmpty()) { - cozeRequest.put("workflow_id", chatWorkflowId); + if (config.getWorkflowId() != null && !config.getWorkflowId().trim().isEmpty()) { + cozeRequest.put("workflow_id", config.getWorkflowId()); } cozeRequest.put("user_id", userId != null ? userId : DEFAULT_USER_ID); - cozeRequest.put("stream", false); + + // 根据配置决定是否使用流式输出 + boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1; + cozeRequest.put("stream", useStream); // 构建消息列表 - 按照 Coze API 标准格式 java.util.List> messages = new java.util.ArrayList<>(); @@ -944,6 +1147,9 @@ public class AiChatServiceImpl implements AiChatService { */ private String waitForChatCompletion(String chatId, String conversationId) { try { + // 获取聊天场景的AI配置 + AiConfig config = getChatAiConfig(); + // 最多等待30秒,每2秒轮询一次 int maxAttempts = 15; int attempt = 0; @@ -952,12 +1158,12 @@ public class AiChatServiceImpl implements AiChatService { log.info("轮询聊天状态,第{}次尝试: chatId={}, conversationId={}", attempt + 1, chatId, conversationId); // 构建状态查询URL - String statusUrl = cozeBaseUrl + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id=" + String statusUrl = config.getApiBaseUrl() + "/v3/chat/retrieve?chat_id=" + chatId + "&conversation_id=" + conversationId; // 构建请求头 HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + cozeApiToken); + headers.set("Authorization", "Bearer " + config.getApiToken()); headers.set("Content-Type", "application/json"); HttpEntity request = new HttpEntity<>(headers); @@ -1009,15 +1215,18 @@ public class AiChatServiceImpl implements AiChatService { */ private String getChatMessages(String chatId, String conversationId) { try { + // 获取聊天场景的AI配置 + AiConfig config = getChatAiConfig(); + log.info("获取聊天消息: chatId={}, conversationId={}", chatId, conversationId); // 构建消息查询URL - String messagesUrl = cozeBaseUrl + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id=" + String messagesUrl = config.getApiBaseUrl() + "/v3/chat/message/list?chat_id=" + chatId + "&conversation_id=" + conversationId; // 构建请求头 HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", "Bearer " + cozeApiToken); + headers.set("Authorization", "Bearer " + config.getApiToken()); headers.set("Content-Type", "application/json"); HttpEntity request = new HttpEntity<>(headers); @@ -1067,16 +1276,22 @@ public class AiChatServiceImpl implements AiChatService { * 构建总结请求 - 使用专门的总结bot和workflow */ private Map buildSummaryRequest(String conversationId, String userMessage, String userId) { + // 获取总结场景的AI配置 + AiConfig config = getSummaryAiConfig(); + Map cozeRequest = new HashMap<>(); - cozeRequest.put("bot_id", summaryBotId != null && !summaryBotId.trim().isEmpty() ? summaryBotId : chatBotId); + cozeRequest.put("bot_id", config.getBotId()); - // 如果有总结workflow_id,则添加 - if (summaryWorkflowId != null && !summaryWorkflowId.trim().isEmpty()) { - cozeRequest.put("workflow_id", summaryWorkflowId); + // 如果有workflow_id,则添加 + if (config.getWorkflowId() != null && !config.getWorkflowId().trim().isEmpty()) { + cozeRequest.put("workflow_id", config.getWorkflowId()); } cozeRequest.put("user_id", userId != null ? userId : DEFAULT_USER_ID); - cozeRequest.put("stream", false); + + // 根据配置决定是否使用流式输出 + boolean useStream = config.getSupportStream() != null && config.getSupportStream() == 1; + cozeRequest.put("stream", useStream); cozeRequest.put("auto_save_history", true); // 如果有会话ID,则添加 @@ -1118,6 +1333,9 @@ public class AiChatServiceImpl implements AiChatService { */ private CozeApiCall createApiCallRecord(String conversationId, String messageId, String userMessage, String userId, String requestType) { + // 获取聊天场景的AI配置 + AiConfig config = getChatAiConfig(); + CozeApiCall apiCall = CozeApiCall.builder() .conversationId(conversationId) .messageId(messageId) // 设置messageId @@ -1125,8 +1343,8 @@ public class AiChatServiceImpl implements AiChatService { .requestType(requestType) .userMessage(userMessage) .userMessageType("text") - .botId(chatBotId) - .workflowId(chatWorkflowId) + .botId(config.getBotId()) + .workflowId(config.getWorkflowId()) .status("pending") .startTime(LocalDateTime.now()) .traceId(UUID.randomUUID().toString()) @@ -1158,6 +1376,9 @@ public class AiChatServiceImpl implements AiChatService { */ private CozeApiCall createSummaryApiCallRecord(String conversationId, String messageId, String userMessage, String userId, String requestType) { + // 获取总结场景的AI配置 + AiConfig config = getSummaryAiConfig(); + CozeApiCall apiCall = CozeApiCall.builder() .conversationId(conversationId) .messageId(messageId) // 设置messageId @@ -1165,8 +1386,8 @@ public class AiChatServiceImpl implements AiChatService { .requestType(requestType) .userMessage(userMessage) .userMessageType("text") - .botId(summaryBotId) - .workflowId(summaryWorkflowId) + .botId(config.getBotId()) + .workflowId(config.getWorkflowId()) .status("pending") .startTime(LocalDateTime.now()) .traceId(UUID.randomUUID().toString()) @@ -1505,4 +1726,36 @@ public class AiChatServiceImpl implements AiChatService { return record; } + /** + * 获取聊天场景的AI配置 + */ + private AiConfig getChatAiConfig() { + AiConfig config = aiConfigService.getBestConfig(USAGE_SCENARIO_CHAT, DEFAULT_ENVIRONMENT); + if (config == null) { + log.error("未找到聊天场景的AI配置"); + throw new RuntimeException("未找到聊天场景的AI配置,请先在管理后台配置"); + } + return config; + } + + /** + * 获取总结场景的AI配置 + */ + private AiConfig getSummaryAiConfig() { + AiConfig config = aiConfigService.getBestConfig(USAGE_SCENARIO_SUMMARY, DEFAULT_ENVIRONMENT); + if (config == null) { + log.warn("未找到总结场景的AI配置,使用聊天配置"); + return getChatAiConfig(); + } + return config; + } + + /** + * 获取配置的API路径 + */ + private String getApiPath(AiConfig config) { + // 默认使用 /v3/chat 路径 + return "/v3/chat"; + } + } \ No newline at end of file diff --git a/web-admin/src/views/aiconfig/AiConfigList.vue b/web-admin/src/views/aiconfig/AiConfigList.vue index e374777..83c0c84 100644 --- a/web-admin/src/views/aiconfig/AiConfigList.vue +++ b/web-admin/src/views/aiconfig/AiConfigList.vue @@ -549,6 +549,28 @@ + +
+ + 启用流式响应 + + + + +
+
+ + + + + - 发送测试请求 + {{ testOptions.useStream ? '发送流式测试' : '发送测试请求' }} 格式化请求 重置 @@ -625,6 +647,7 @@