Commit 5655ae925ccda0492d5b0c5d34efb901145a29e1
1 parent
7ef73b7d
【代码优化】AI:适配 Spring AI 1.0.6 对 OpenAI 的逻辑
Showing
8 changed files
with
33 additions
and
27 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| @@ -98,7 +98,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -98,7 +98,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 98 | ChatResponse chatResponse = chatModel.call(prompt); | 98 | ChatResponse chatResponse = chatModel.call(prompt); |
| 99 | 99 | ||
| 100 | // 3.4 段式返回 | 100 | // 3.4 段式返回 |
| 101 | - String newContent = chatResponse.getResult().getOutput().getContent(); | 101 | + String newContent = chatResponse.getResult().getOutput().getText(); |
| 102 | chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent)); | 102 | chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent)); |
| 103 | return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) | 103 | return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) |
| 104 | .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)); | 104 | .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)); |
| @@ -136,7 +136,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -136,7 +136,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 136 | // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题 | 136 | // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题 |
| 137 | StringBuffer contentBuffer = new StringBuffer(); | 137 | StringBuffer contentBuffer = new StringBuffer(); |
| 138 | return streamResponse.map(chunk -> { | 138 | return streamResponse.map(chunk -> { |
| 139 | - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; | 139 | + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; |
| 140 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 | 140 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 |
| 141 | contentBuffer.append(newContent); | 141 | contentBuffer.append(newContent); |
| 142 | // 响应结果 | 142 | // 响应结果 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
| @@ -60,7 +60,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | @@ -60,7 +60,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | ||
| 60 | List<Document> documents = loader.get(); | 60 | List<Document> documents = loader.get(); |
| 61 | Document document = CollUtil.getFirst(documents); | 61 | Document document = CollUtil.getFirst(documents); |
| 62 | // 1.2 文档记录入库 | 62 | // 1.2 文档记录入库 |
| 63 | - String content = document.getContent(); | 63 | + String content = document.getText(); |
| 64 | AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) | 64 | AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) |
| 65 | .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length()) | 65 | .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length()) |
| 66 | .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); | 66 | .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); |
| @@ -77,9 +77,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | @@ -77,9 +77,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic | ||
| 77 | List<Document> segments = tokenTextSplitter.apply(documents); | 77 | List<Document> segments = tokenTextSplitter.apply(documents); |
| 78 | // 2.2 分段内容入库 | 78 | // 2.2 分段内容入库 |
| 79 | List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments, | 79 | List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments, |
| 80 | - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) | 80 | + segment -> new AiKnowledgeSegmentDO().setContent(segment.getText()).setDocumentId(documentId) |
| 81 | .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) | 81 | .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) |
| 82 | - .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) | 82 | + .setTokens(tokenCountEstimator.estimate(segment.getText())).setWordCount(segment.getText().length()) |
| 83 | .setStatus(CommonStatusEnum.ENABLE.getStatus())); | 83 | .setStatus(CommonStatusEnum.ENABLE.getStatus())); |
| 84 | segmentMapper.insertBatch(segmentDOList); | 84 | segmentMapper.insertBatch(segmentDOList); |
| 85 | 85 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
| @@ -106,10 +106,12 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService | @@ -106,10 +106,12 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService | ||
| 106 | VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); | 106 | VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); |
| 107 | 107 | ||
| 108 | // 3.1 向量检索 | 108 | // 3.1 向量检索 |
| 109 | - List<Document> documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent()) | ||
| 110 | - .withTopK(knowledge.getTopK()) | ||
| 111 | - .withSimilarityThreshold(knowledge.getSimilarityThreshold()) | ||
| 112 | - .withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build())); | 109 | + List<Document> documentList = vectorStore.similaritySearch(SearchRequest.builder() |
| 110 | + .query(reqVO.getContent()) | ||
| 111 | + .topK(knowledge.getTopK()) | ||
| 112 | + .similarityThreshold(knowledge.getSimilarityThreshold()) | ||
| 113 | + .filterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()) | ||
| 114 | + .build()); | ||
| 113 | if (CollUtil.isEmpty(documentList)) { | 115 | if (CollUtil.isEmpty(documentList)) { |
| 114 | return ListUtil.empty(); | 116 | return ListUtil.empty(); |
| 115 | } | 117 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
| @@ -85,7 +85,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { | @@ -85,7 +85,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { | ||
| 85 | // 3.2 流式返回 | 85 | // 3.2 流式返回 |
| 86 | StringBuffer contentBuffer = new StringBuffer(); | 86 | StringBuffer contentBuffer = new StringBuffer(); |
| 87 | return streamResponse.map(chunk -> { | 87 | return streamResponse.map(chunk -> { |
| 88 | - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; | 88 | + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; |
| 89 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 | 89 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 |
| 90 | contentBuffer.append(newContent); | 90 | contentBuffer.append(newContent); |
| 91 | // 响应结果 | 91 | // 响应结果 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
| @@ -92,7 +92,7 @@ public class AiWriteServiceImpl implements AiWriteService { | @@ -92,7 +92,7 @@ public class AiWriteServiceImpl implements AiWriteService { | ||
| 92 | // 3.2 流式返回 | 92 | // 3.2 流式返回 |
| 93 | StringBuffer contentBuffer = new StringBuffer(); | 93 | StringBuffer contentBuffer = new StringBuffer(); |
| 94 | return streamResponse.map(chunk -> { | 94 | return streamResponse.map(chunk -> { |
| 95 | - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; | 95 | + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; |
| 96 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 | 96 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 |
| 97 | contentBuffer.append(newContent); | 97 | contentBuffer.append(newContent); |
| 98 | // 响应结果 | 98 | // 响应结果 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/OpenAIChatModelTests.java
| @@ -22,11 +22,16 @@ import java.util.List; | @@ -22,11 +22,16 @@ import java.util.List; | ||
| 22 | */ | 22 | */ |
| 23 | public class OpenAIChatModelTests { | 23 | public class OpenAIChatModelTests { |
| 24 | 24 | ||
| 25 | - private final OpenAiApi openAiApi = new OpenAiApi( | ||
| 26 | - "https://api.holdai.top", | ||
| 27 | - "sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf"); | ||
| 28 | - private final OpenAiChatModel chatModel = new OpenAiChatModel(openAiApi, | ||
| 29 | - OpenAiChatOptions.builder().model(OpenAiApi.ChatModel.GPT_4_O).build()); | 25 | + private static final OpenAiChatModel chatModel = OpenAiChatModel.builder() |
| 26 | + .openAiApi(OpenAiApi.builder() | ||
| 27 | + .baseUrl("https://api.holdai.top") | ||
| 28 | + .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") // apiKey | ||
| 29 | + .build()) | ||
| 30 | + .defaultOptions(OpenAiChatOptions.builder() | ||
| 31 | + .model(OpenAiApi.ChatModel.GPT_4_O) // 模型 | ||
| 32 | + .temperature(0.7) | ||
| 33 | + .build()) | ||
| 34 | + .build(); | ||
| 30 | 35 | ||
| 31 | @Test | 36 | @Test |
| 32 | @Disabled | 37 | @Disabled |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/OpenAiImageModelTests.java
| @@ -5,8 +5,11 @@ import org.junit.jupiter.api.Test; | @@ -5,8 +5,11 @@ import org.junit.jupiter.api.Test; | ||
| 5 | import org.springframework.ai.image.ImageOptions; | 5 | import org.springframework.ai.image.ImageOptions; |
| 6 | import org.springframework.ai.image.ImagePrompt; | 6 | import org.springframework.ai.image.ImagePrompt; |
| 7 | import org.springframework.ai.image.ImageResponse; | 7 | import org.springframework.ai.image.ImageResponse; |
| 8 | +import org.springframework.ai.openai.OpenAiChatModel; | ||
| 9 | +import org.springframework.ai.openai.OpenAiChatOptions; | ||
| 8 | import org.springframework.ai.openai.OpenAiImageModel; | 10 | import org.springframework.ai.openai.OpenAiImageModel; |
| 9 | import org.springframework.ai.openai.OpenAiImageOptions; | 11 | import org.springframework.ai.openai.OpenAiImageOptions; |
| 12 | +import org.springframework.ai.openai.api.OpenAiApi; | ||
| 10 | import org.springframework.ai.openai.api.OpenAiImageApi; | 13 | import org.springframework.ai.openai.api.OpenAiImageApi; |
| 11 | import org.springframework.web.client.RestClient; | 14 | import org.springframework.web.client.RestClient; |
| 12 | 15 | ||
| @@ -17,11 +20,10 @@ import org.springframework.web.client.RestClient; | @@ -17,11 +20,10 @@ import org.springframework.web.client.RestClient; | ||
| 17 | */ | 20 | */ |
| 18 | public class OpenAiImageModelTests { | 21 | public class OpenAiImageModelTests { |
| 19 | 22 | ||
| 20 | - private final OpenAiImageApi imageApi = new OpenAiImageApi( | ||
| 21 | - "https://api.holdai.top", | ||
| 22 | - "sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf", | ||
| 23 | - RestClient.builder()); | ||
| 24 | - private final OpenAiImageModel imageModel = new OpenAiImageModel(imageApi); | 23 | + private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder() |
| 24 | + .baseUrl("https://api.holdai.top") // apiKey | ||
| 25 | + .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") | ||
| 26 | + .build()); | ||
| 25 | 27 | ||
| 26 | @Test | 28 | @Test |
| 27 | @Disabled | 29 | @Disabled |
yudao-server/src/main/resources/application.yaml
| @@ -163,7 +163,7 @@ spring: | @@ -163,7 +163,7 @@ spring: | ||
| 163 | zhipuai: # 智谱 AI | 163 | zhipuai: # 智谱 AI |
| 164 | api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs | 164 | api-key: 32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs |
| 165 | openai: # OpenAI 官方 | 165 | openai: # OpenAI 官方 |
| 166 | - api-key: sk-yzKea6d8e8212c3bdd99f9f44ced1cae37c097e5aa3BTS7z | 166 | + api-key: sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17 |
| 167 | base-url: https://api.gptsapi.net | 167 | base-url: https://api.gptsapi.net |
| 168 | azure: # OpenAI 微软 | 168 | azure: # OpenAI 微软 |
| 169 | openai: | 169 | openai: |
| @@ -175,11 +175,8 @@ spring: | @@ -175,11 +175,8 @@ spring: | ||
| 175 | model: llama3 | 175 | model: llama3 |
| 176 | stabilityai: | 176 | stabilityai: |
| 177 | api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx | 177 | api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx |
| 178 | - cloud: | ||
| 179 | - ai: | ||
| 180 | - tongyi: # 通义千问 | ||
| 181 | - tongyi: | ||
| 182 | - api-key: sk-Zsd81gZYg7 | 178 | + dashscope: # 通义千问 |
| 179 | + api-key: sk-71800982914041848008480000000000 | ||
| 183 | 180 | ||
| 184 | yudao: | 181 | yudao: |
| 185 | ai: | 182 | ai: |