Commit 99b6f240923b05357b5f31f37dd9907764d6012a

Authored by YunaiV
1 parent 041a00b2

【修复】AI:解决 spring-ai 与 webflux 集成时,SSE 存在乱序的问题

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
... ... @@ -6,7 +6,7 @@ Authorization: {{token}}
6 6  
7 7 {
8 8 "conversationId": "1781604279872581649",
9   - "content": "中国好看吗?"
  9 + "content": "你是 OpenAI 么?"
10 10 }
11 11  
12 12  
... ... @@ -16,11 +16,10 @@ Content-Type: application/json
16 16 Authorization: {{token}}
17 17  
18 18 {
19   - "conversationId": "1781604279872581651",
20   - "content": "苹果是什么颜色?"
  19 + "conversationId": "1781604279872581690",
  20 + "content": "1+1=?"
21 21 }
22 22  
23   -
24 23 ### message list
25 24 GET {{baseUrl}}/admin-api/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581649
26 25 Authorization: {{token}}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java
... ... @@ -15,12 +15,11 @@ import java.util.List;
15 15 @Mapper
16 16 public interface AiChatConversationMapper extends BaseMapperX<AiChatConversationDO> {
17 17  
  18 + // TODO @fan:建议这里不排序哈;交给他们前端排序
18 19 default List<AiChatConversationDO> selectListByUserId(Long userId) {
19   - return selectList(
20   - new LambdaQueryWrapperX<AiChatConversationDO>()
21   - .eq(AiChatConversationDO::getUserId, userId)
22   - .orderByAsc(AiChatConversationDO::getCreateTime)
23   - );
  20 + return selectList(new LambdaQueryWrapperX<AiChatConversationDO>()
  21 + .eq(AiChatConversationDO::getUserId, userId)
  22 + .orderByAsc(AiChatConversationDO::getCreateTime));
24 23 }
25 24  
26 25 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
... ... @@ -32,6 +32,7 @@ import lombok.extern.slf4j.Slf4j;
32 32 import org.springframework.stereotype.Service;
33 33 import org.springframework.transaction.annotation.Transactional;
34 34 import reactor.core.publisher.Flux;
  35 +import reactor.core.scheduler.Schedulers;
35 36  
36 37 import java.time.LocalDateTime;
37 38 import java.util.*;
... ... @@ -125,9 +126,10 @@ public class AiChatServiceImpl implements AiChatService {
125 126 Flux<ChatResponse> streamResponse = chatClient.stream(prompt);
126 127  
127 128 // 3.3 流式返回
  129 + // 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题
128 130 StringBuffer contentBuffer = new StringBuffer();
129   - return streamResponse.map(response -> {
130   - String newContent = response.getResult() != null ? response.getResult().getOutput().getContent() : null;
  131 + return streamResponse.publishOn(Schedulers.immediate()).map(chunk -> {
  132 + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
131 133 newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
132 134 contentBuffer.append(newContent);
133 135 // 响应结果
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/openAiImage/OpenAiImageClientTests.java
... ... @@ -71,21 +71,4 @@ public class OpenAiImageClientTests {
71 71 }
72 72 }
73 73  
74   - public static void main(String[] args) {
75   -// OpenAiApi api = new OpenAiApi("https://api.gptsapi.net", "sk-yzKea6d8e8212c3bdd99f9f44ced1cae37c097e5aa3BTS7z");
76   -// OpenAiApi api = new OpenAiApi("https://openkey.cloud", "sk-QmgIIPc5xiYd8lPb076b1b7774Ea49Af9eD2Ef172c8f7e43");
77   - OpenAiApi api = new OpenAiApi("https://api.chatanywhere.tech", "sk-gkgfYxhX9FxyZJznwxRZSJwKeGQYNPDVWjhby2PRRf17GHeT");
78   - OpenAiChatClient client = new OpenAiChatClient(api);
79   -// String result = client.call("未来,英文是什么?");
80   -// System.out.println(result);
81   - Flux<String> result = client.stream("未来,英文是什么?");
82   - result.map(new Function<String, String>() {
83   - @Override
84   - public String apply(String s) {
85   - System.out.println(s);
86   - return s;
87   - }
88   - }).blockLast();
89   - }
90   -
91 74 }
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -156,6 +156,9 @@ spring.ai:
156 156 # base-url: https://api.chatanywhere.tech
157 157 api-key: sk-yzKea6d8e8212c3bdd99f9f44ced1cae37c097e5aa3BTS7z
158 158 base-url: https://api.gptsapi.net
  159 +# chat:
  160 +# options:
  161 +# model: gpt-4-0125-preview
159 162  
160 163 yudao.ai:
161 164 yiyan:
... ...